From cdd066dafe95bb777503212781a39405797de724 Mon Sep 17 00:00:00 2001 From: Hein Date: Thu, 3 Sep 2026 22:12:36 +0200 Subject: [PATCH] fix(sqltypes): serialize SqlTimeStamp as RFC3339 Marshal JSON/YAML/XML and driver Value now emit time.RFC3339 instead of the offset-less "2006-01-02T15:04:05" layout; zero-time guards compare against the RFC3339 zero value. Parsing is unchanged and still accepts bare datetimes. --- pkg/sqltypes/sql_types.go | 16 ++++++++-------- pkg/sqltypes/sql_types_test.go | 2 +- pkg/sqltypes/sql_types_yaml_xml_test.go | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/sqltypes/sql_types.go b/pkg/sqltypes/sql_types.go index 8c6b83e..f3e18fa 100644 --- a/pkg/sqltypes/sql_types.go +++ b/pkg/sqltypes/sql_types.go @@ -445,21 +445,21 @@ type ( SqlUUID = SqlNull[uuid.UUID] ) -// SqlTimeStamp - Timestamp with custom formatting (YYYY-MM-DDTHH:MM:SS). +// SqlTimeStamp - Timestamp serialized as RFC3339. type SqlTimeStamp struct{ SqlNull[time.Time] } func (t SqlTimeStamp) MarshalJSON() ([]byte, error) { if !t.Valid || t.Val.IsZero() || t.Val.Before(time.Date(0o002, 1, 1, 0, 0, 0, 0, time.UTC)) { return []byte("null"), nil } - return fmt.Appendf(nil, `"%s"`, t.Val.Format("2006-01-02T15:04:05")), nil + return fmt.Appendf(nil, `"%s"`, t.Val.Format(time.RFC3339)), nil } func (t *SqlTimeStamp) UnmarshalJSON(b []byte) error { if err := t.SqlNull.UnmarshalJSON(b); err != nil { return err } - if t.Valid && (t.Val.IsZero() || t.Val.Format("2006-01-02T15:04:05") == "0001-01-01T00:00:00") { + if t.Valid && (t.Val.IsZero() || t.Val.Format(time.RFC3339) == "0001-01-01T00:00:00Z") { t.Valid = false } return nil @@ -469,21 +469,21 @@ func (t SqlTimeStamp) Value() (driver.Value, error) { if !t.Valid || t.Val.IsZero() || t.Val.Before(time.Date(0o002, 1, 1, 0, 0, 0, 0, time.UTC)) { return nil, nil } - return t.Val.Format("2006-01-02T15:04:05"), nil + return t.Val.Format(time.RFC3339), nil } func (t SqlTimeStamp) MarshalYAML() (any, error) { if !t.Valid || t.Val.IsZero() || t.Val.Before(time.Date(0o002, 1, 1, 0, 0, 0, 0, time.UTC)) { return nil, nil } - return t.Val.Format("2006-01-02T15:04:05"), nil + return t.Val.Format(time.RFC3339), nil } func (t *SqlTimeStamp) UnmarshalYAML(value *yaml.Node) error { if err := t.SqlNull.UnmarshalYAML(value); err != nil { return err } - if t.Valid && (t.Val.IsZero() || t.Val.Format("2006-01-02T15:04:05") == "0001-01-01T00:00:00") { + if t.Valid && (t.Val.IsZero() || t.Val.Format(time.RFC3339) == "0001-01-01T00:00:00Z") { t.Valid = false } return nil @@ -493,7 +493,7 @@ func (t SqlTimeStamp) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if !t.Valid || t.Val.IsZero() || t.Val.Before(time.Date(0o002, 1, 1, 0, 0, 0, 0, time.UTC)) { return e.EncodeElement("", start) } - return e.EncodeElement(t.Val.Format("2006-01-02T15:04:05"), start) + return e.EncodeElement(t.Val.Format(time.RFC3339), start) } func (t *SqlTimeStamp) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { @@ -511,7 +511,7 @@ func (t *SqlTimeStamp) UnmarshalXML(d *xml.Decoder, start xml.StartElement) erro return err } t.Val = tm - t.Valid = !tm.IsZero() && tm.Format("2006-01-02T15:04:05") != "0001-01-01T00:00:00" + t.Valid = !tm.IsZero() && tm.Format(time.RFC3339) != "0001-01-01T00:00:00Z" return nil } diff --git a/pkg/sqltypes/sql_types_test.go b/pkg/sqltypes/sql_types_test.go index bad8dd3..b508bf9 100644 --- a/pkg/sqltypes/sql_types_test.go +++ b/pkg/sqltypes/sql_types_test.go @@ -178,7 +178,7 @@ func TestSqlTimeStamp_JSON(t *testing.T) { if err != nil { t.Fatalf("Marshal failed: %v", err) } - expected := `"2024-01-15T10:30:45"` + expected := `"2024-01-15T10:30:45Z"` if string(data) != expected { t.Errorf("expected %s, got %s", expected, string(data)) } diff --git a/pkg/sqltypes/sql_types_yaml_xml_test.go b/pkg/sqltypes/sql_types_yaml_xml_test.go index 098bb8b..6c3b8da 100644 --- a/pkg/sqltypes/sql_types_yaml_xml_test.go +++ b/pkg/sqltypes/sql_types_yaml_xml_test.go @@ -195,7 +195,7 @@ func TestSqlTimeStamp_YAML(t *testing.T) { if err != nil { t.Fatalf("Marshal failed: %v", err) } - if string(data) != "2024-06-15T09:30:00\n" { + if string(data) != "\"2024-06-15T09:30:00Z\"\n" { t.Errorf("unexpected YAML: %q", string(data)) } var ts2 SqlTimeStamp