package notion import ( "encoding/json" "errors" "time" ) // Length of a date string, e.g. `2006-01-02`. const dateLength = 10 // DateTimeFormat is used when calling time.Parse, using RFC3339 with microsecond // precision, which is what the Notion API returns in JSON response data. const DateTimeFormat = "2006-01-02T15:04:05.999Z07:00" // DateTime represents a Notion date property with optional time. type DateTime struct { time.Time hasTime bool } // ParseDateTime parses an RFC3339 formatted string with optional time. func ParseDateTime(value string) (DateTime, error) { if len(value) > len(DateTimeFormat) { return DateTime{}, errors.New("invalid datetime string") } t, err := time.Parse(DateTimeFormat[:len(value)], value) if err != nil { return DateTime{}, err } dt := DateTime{ Time: t, hasTime: len(value) > dateLength, } return dt, nil } // UnmarshalJSON implements json.Unmarshaler. func (dt *DateTime) UnmarshalJSON(b []byte) error { if len(b) < 2 { return errors.New("invalid datetime string") } parsed, err := ParseDateTime(string(b[1 : len(b)-1])) if err != nil { return err } *dt = parsed return nil } // MarshalJSON implements json.Marshaler. It returns an RFC399 formatted string, // using microsecond precision () func (dt DateTime) MarshalJSON() ([]byte, error) { if dt.hasTime { return json.Marshal(dt.Time) } return []byte(`"` + dt.Time.Format(DateTimeFormat[:dateLength]) + `"`), nil } // NewDateTime returns a new DateTime. If `haseTime` is true, time is included // when encoding to JSON. func NewDateTime(t time.Time, hasTime bool) DateTime { var tt time.Time if hasTime { tt = t } else { tt = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.UTC) } return DateTime{ Time: tt, hasTime: hasTime, } } // HasTime returns true if the datetime was parsed from a string that included time. func (dt *DateTime) HasTime() bool { return dt.hasTime } // Equal returns true if both DateTime values have equal underlying time.Time and // hasTime fields. func (dt DateTime) Equal(value DateTime) bool { if !dt.Time.Equal(value.Time) { return false } return dt.hasTime == value.hasTime }