package model import ( "fmt" "time" "gopkg.in/yaml.v3" ) // timeLayouts are tried in order when parsing a persisted timestamp. The // first two cover what this Go implementation itself writes (time zone // aware); the rest cover what the original Python implementation wrote via // datetime.now().isoformat() — no time zone, and microseconds included only // when non-zero (Go's parser accepts an optional fractional-seconds suffix // even when the layout doesn't specify one, so a single no-fraction layout // handles both cases). var timeLayouts = []string{ time.RFC3339Nano, time.RFC3339, "2006-01-02T15:04:05", } // Time wraps time.Time to support reading timestamps in both this Go // implementation's own format and the original Python implementation's // timezone-naive datetime.isoformat() format (e.g. // "2026-03-19T18:21:47.740090"), which Go's default time.Time YAML/text // decoding rejects outright since it strictly requires RFC3339 (with a zone // offset). type Time struct { time.Time } // UnmarshalYAML tries each supported layout in turn. func (t *Time) UnmarshalYAML(value *yaml.Node) error { var s string if err := value.Decode(&s); err != nil { return err } if s == "" { t.Time = time.Time{} return nil } var lastErr error for _, layout := range timeLayouts { parsed, err := time.Parse(layout, s) if err == nil { t.Time = parsed return nil } lastErr = err } return fmt.Errorf("cannot parse time %q: %w", s, lastErr) } // MarshalYAML writes the timestamp in RFC3339Nano (timezone-aware) format. func (t Time) MarshalYAML() (interface{}, error) { if t.Time.IsZero() { return nil, nil } return t.Time.Format(time.RFC3339Nano), nil }