a1284075aa
Validate constructors, strict HH:MM:SS parse, UTC ClockTimeFromTime, ISODuration Value/Scan, Compare and TextMarshaler, codec.go split. Co-authored-by: Cursor <cursoragent@cursor.com>
211 lines
4.4 KiB
Go
211 lines
4.4 KiB
Go
package clocktime
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
)
|
|
|
|
func marshalJSONString(s string) ([]byte, error) {
|
|
return json.Marshal(s)
|
|
}
|
|
|
|
func unmarshalJSONString(data []byte) (string, error) {
|
|
var s string
|
|
if err := json.Unmarshal(data, &s); err != nil {
|
|
return "", err
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// MarshalJSON encodes ClockTime as a JSON string HH:MM:SS.
|
|
func (t ClockTime) MarshalJSON() ([]byte, error) {
|
|
return marshalJSONString(t.String())
|
|
}
|
|
|
|
// UnmarshalJSON decodes a JSON string into ClockTime.
|
|
func (t *ClockTime) UnmarshalJSON(data []byte) error {
|
|
if string(data) == "null" {
|
|
*t = ClockTime{}
|
|
return nil
|
|
}
|
|
s, err := unmarshalJSONString(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
parsed, err := ParseClockTime(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*t = parsed
|
|
return nil
|
|
}
|
|
|
|
// MarshalText implements encoding.TextMarshaler.
|
|
func (t ClockTime) MarshalText() ([]byte, error) {
|
|
return []byte(t.String()), nil
|
|
}
|
|
|
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
|
func (t *ClockTime) UnmarshalText(text []byte) error {
|
|
parsed, err := ParseClockTime(string(text))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*t = parsed
|
|
return nil
|
|
}
|
|
|
|
// MarshalGQL implements gqlgen's Marshaler interface.
|
|
func (t ClockTime) MarshalGQL(w io.Writer) {
|
|
fmt.Fprint(w, "\""+t.String()+"\"")
|
|
}
|
|
|
|
// UnmarshalGQL implements gqlgen's Unmarshaler interface.
|
|
func (t *ClockTime) UnmarshalGQL(value any) error {
|
|
if value == nil {
|
|
*t = ClockTime{}
|
|
return nil
|
|
}
|
|
str, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("ClockTime must be a string, got %T", value)
|
|
}
|
|
parsed, err := ParseClockTime(str)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*t = parsed
|
|
return nil
|
|
}
|
|
|
|
// Value implements driver.Valuer for SQL TIME columns as HH:MM:SS bytes.
|
|
func (t ClockTime) Value() (driver.Value, error) {
|
|
return []byte(t.String()), nil
|
|
}
|
|
|
|
// Scan implements sql.Scanner for time.Time, []byte, and string.
|
|
func (t *ClockTime) Scan(value any) error {
|
|
if value == nil {
|
|
*t = ClockTime{}
|
|
return nil
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case time.Time:
|
|
*t = ClockTimeFromTime(v)
|
|
case []byte:
|
|
parsed, err := ParseClockTime(string(v))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse ClockTime: %w", err)
|
|
}
|
|
*t = parsed
|
|
case string:
|
|
parsed, err := ParseClockTime(v)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse ClockTime: %w", err)
|
|
}
|
|
*t = parsed
|
|
default:
|
|
return fmt.Errorf("ClockTime.Scan: unsupported type %T", value)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON encodes ISODuration as a JSON ISO 8601 string.
|
|
func (d ISODuration) MarshalJSON() ([]byte, error) {
|
|
return marshalJSONString(d.String())
|
|
}
|
|
|
|
// UnmarshalJSON decodes a JSON ISO 8601 duration string.
|
|
func (d *ISODuration) UnmarshalJSON(data []byte) error {
|
|
if string(data) == "null" {
|
|
*d = ISODuration(0)
|
|
return nil
|
|
}
|
|
s, err := unmarshalJSONString(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
parsed, err := ParseISODuration(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*d = parsed
|
|
return nil
|
|
}
|
|
|
|
// MarshalText implements encoding.TextMarshaler.
|
|
func (d ISODuration) MarshalText() ([]byte, error) {
|
|
return []byte(d.String()), nil
|
|
}
|
|
|
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
|
func (d *ISODuration) UnmarshalText(text []byte) error {
|
|
parsed, err := ParseISODuration(string(text))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*d = parsed
|
|
return nil
|
|
}
|
|
|
|
// MarshalGQL implements gqlgen's Marshaler interface.
|
|
func (d ISODuration) MarshalGQL(w io.Writer) {
|
|
fmt.Fprint(w, "\""+d.String()+"\"")
|
|
}
|
|
|
|
// UnmarshalGQL implements gqlgen's Unmarshaler interface.
|
|
func (d *ISODuration) UnmarshalGQL(value any) error {
|
|
if value == nil {
|
|
*d = ISODuration(0)
|
|
return nil
|
|
}
|
|
s, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("ISODuration must be a string, got %T", value)
|
|
}
|
|
parsed, err := ParseISODuration(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*d = parsed
|
|
return nil
|
|
}
|
|
|
|
// Value stores ISODuration as nanoseconds in the database.
|
|
func (d ISODuration) Value() (driver.Value, error) {
|
|
return int64(d), nil
|
|
}
|
|
|
|
// Scan reads ISODuration from int64 nanoseconds or an ISO 8601 string.
|
|
func (d *ISODuration) Scan(value any) error {
|
|
if value == nil {
|
|
*d = ISODuration(0)
|
|
return nil
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case int64:
|
|
*d = ISODuration(v)
|
|
case []byte:
|
|
parsed, err := ParseISODuration(string(v))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*d = parsed
|
|
case string:
|
|
parsed, err := ParseISODuration(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*d = parsed
|
|
default:
|
|
return fmt.Errorf("ISODuration.Scan: unsupported type %T", value)
|
|
}
|
|
return nil
|
|
}
|