package model import ( "database/sql/driver" "encoding/json" "errors" "fmt" "time" ) const dateLayout = "2006-01-02" // Date 仅日期字段(YYYY-MM-DD),零值表示未设置,JSON 序列化为 null。 type Date struct { time.Time } // IsZero 是否未设置。 func (d Date) IsZero() bool { return d.Time.IsZero() } // Value 实现 driver.Valuer:零值写入 NULL。 func (d Date) Value() (driver.Value, error) { if d.Time.IsZero() { return nil, nil } return d.Time.Format(dateLayout), nil } // Scan 实现 sql.Scanner,兼容 time.Time、字符串与 []byte。 func (d *Date) Scan(value any) error { switch v := value.(type) { case nil: d.Time = time.Time{} case time.Time: d.Time = v case string: return d.parse(v) case []byte: return d.parse(string(v)) default: return fmt.Errorf("unsupported date value: %T", value) } return nil } func (d *Date) parse(value string) error { if value == "" { d.Time = time.Time{} return nil } for _, layout := range []string{dateLayout, time.RFC3339} { if parsed, err := time.Parse(layout, value); err == nil { d.Time = parsed return nil } } return errors.New("invalid date: " + value) } // MarshalJSON 输出 YYYY-MM-DD,未设置时输出 null。 func (d Date) MarshalJSON() ([]byte, error) { if d.Time.IsZero() { return []byte("null"), nil } return json.Marshal(d.Time.Format(dateLayout)) } // UnmarshalJSON 支持 null、空串与 YYYY-MM-DD。 func (d *Date) UnmarshalJSON(data []byte) error { if string(data) == "null" { d.Time = time.Time{} return nil } var value string if err := json.Unmarshal(data, &value); err != nil { return err } return d.parse(value) }