// FILE: ./internal/sources/common/id.go package common import ( "fmt" "strings" "time" ) // ChooseEventID applies weatherfeeder's opinionated Event.ID policy: // // - If upstream provides an ID, use it (trimmed). // - Otherwise, ID is ":" when available. // - If EffectiveAt is unavailable, fall back to ":". // // Timestamps are encoded as RFC3339Nano in UTC. func ChooseEventID(upstreamID, sourceName string, effectiveAt *time.Time, emittedAt time.Time) string { if id := strings.TrimSpace(upstreamID); id != "" { return id } src := strings.TrimSpace(sourceName) if src == "" { src = "UNKNOWN_SOURCE" } // Prefer EffectiveAt for dedupe friendliness. if effectiveAt != nil && !effectiveAt.IsZero() { return fmt.Sprintf("%s:%s", src, effectiveAt.UTC().Format(time.RFC3339Nano)) } // Fall back to EmittedAt (still stable within a poll invocation). t := emittedAt.UTC() if t.IsZero() { t = time.Now().UTC() } return fmt.Sprintf("%s:%s", src, t.Format(time.RFC3339Nano)) }