feat: migrate to golang

This commit is contained in:
2026-07-19 20:01:38 +08:00
parent 302d4e6bb5
commit a2630df9e0
69 changed files with 4750 additions and 3369 deletions

62
internal/model/time.go Normal file
View File

@ -0,0 +1,62 @@
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
}