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.
This commit is contained in:
@@ -445,21 +445,21 @@ type (
|
|||||||
SqlUUID = SqlNull[uuid.UUID]
|
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] }
|
type SqlTimeStamp struct{ SqlNull[time.Time] }
|
||||||
|
|
||||||
func (t SqlTimeStamp) MarshalJSON() ([]byte, error) {
|
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)) {
|
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 []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 {
|
func (t *SqlTimeStamp) UnmarshalJSON(b []byte) error {
|
||||||
if err := t.SqlNull.UnmarshalJSON(b); err != nil {
|
if err := t.SqlNull.UnmarshalJSON(b); err != nil {
|
||||||
return err
|
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
|
t.Valid = false
|
||||||
}
|
}
|
||||||
return nil
|
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)) {
|
if !t.Valid || t.Val.IsZero() || t.Val.Before(time.Date(0o002, 1, 1, 0, 0, 0, 0, time.UTC)) {
|
||||||
return nil, nil
|
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) {
|
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)) {
|
if !t.Valid || t.Val.IsZero() || t.Val.Before(time.Date(0o002, 1, 1, 0, 0, 0, 0, time.UTC)) {
|
||||||
return nil, nil
|
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 {
|
func (t *SqlTimeStamp) UnmarshalYAML(value *yaml.Node) error {
|
||||||
if err := t.SqlNull.UnmarshalYAML(value); err != nil {
|
if err := t.SqlNull.UnmarshalYAML(value); err != nil {
|
||||||
return err
|
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
|
t.Valid = false
|
||||||
}
|
}
|
||||||
return nil
|
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)) {
|
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("", 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 {
|
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
|
return err
|
||||||
}
|
}
|
||||||
t.Val = tm
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ func TestSqlTimeStamp_JSON(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Marshal failed: %v", err)
|
t.Fatalf("Marshal failed: %v", err)
|
||||||
}
|
}
|
||||||
expected := `"2024-01-15T10:30:45"`
|
expected := `"2024-01-15T10:30:45Z"`
|
||||||
if string(data) != expected {
|
if string(data) != expected {
|
||||||
t.Errorf("expected %s, got %s", expected, string(data))
|
t.Errorf("expected %s, got %s", expected, string(data))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ func TestSqlTimeStamp_YAML(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Marshal failed: %v", err)
|
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))
|
t.Errorf("unexpected YAML: %q", string(data))
|
||||||
}
|
}
|
||||||
var ts2 SqlTimeStamp
|
var ts2 SqlTimeStamp
|
||||||
|
|||||||
Reference in New Issue
Block a user