github.com/seeker-insurance/kit@v0.0.13/db/null/zero/time.go (about) 1 package zero 2 3 import ( 4 "database/sql/driver" 5 "encoding/json" 6 "fmt" 7 "reflect" 8 "time" 9 ) 10 11 // Time is a nullable time.Time. 12 // JSON marshals to the zero value for time.Time if null. 13 // Considered to be null to SQL if zero. 14 type Time struct { 15 Time time.Time 16 Valid bool 17 } 18 19 // Scan implements Scanner interface. 20 func (t *Time) Scan(value interface{}) error { 21 var err error 22 switch x := value.(type) { 23 case time.Time: 24 t.Time = x 25 case nil: 26 t.Valid = false 27 return nil 28 default: 29 err = fmt.Errorf("null: cannot scan type %T into null.Time: %v", value, value) 30 } 31 t.Valid = err == nil 32 return err 33 } 34 35 // Value implements the driver Valuer interface. 36 func (t Time) Value() (driver.Value, error) { 37 if !t.Valid { 38 return nil, nil 39 } 40 return t.Time, nil 41 } 42 43 // NewTime creates a new Time. 44 func NewTime(t time.Time, valid bool) Time { 45 return Time{ 46 Time: t, 47 Valid: valid, 48 } 49 } 50 51 // TimeFrom creates a new Time that will 52 // be null if t is the zero value. 53 func TimeFrom(t time.Time) Time { 54 return NewTime(t, !t.IsZero()) 55 } 56 57 // TimeFromPtr creates a new Time that will 58 // be null if t is nil or *t is the zero value. 59 func TimeFromPtr(t *time.Time) Time { 60 if t == nil { 61 return NewTime(time.Time{}, false) 62 } 63 return TimeFrom(*t) 64 } 65 66 // MarshalJSON implements json.Marshaler. 67 // It will encode the zero value of time.Time 68 // if this time is invalid. 69 func (t Time) MarshalJSON() ([]byte, error) { 70 if !t.Valid { 71 return (time.Time{}).MarshalJSON() 72 } 73 return t.Time.MarshalJSON() 74 } 75 76 // UnmarshalJSON implements json.Unmarshaler. 77 // It supports string, object (e.g. pq.NullTime and friends) 78 // and null input. 79 func (t *Time) UnmarshalJSON(data []byte) error { 80 var err error 81 var v interface{} 82 if err = json.Unmarshal(data, &v); err != nil { 83 return err 84 } 85 switch x := v.(type) { 86 case string: 87 var ti time.Time 88 if err = ti.UnmarshalJSON(data); err != nil { 89 return err 90 } 91 *t = TimeFrom(ti) 92 return nil 93 case map[string]interface{}: 94 ti, tiOK := x["Time"].(string) 95 valid, validOK := x["Valid"].(bool) 96 if !tiOK || !validOK { 97 return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"]) 98 } 99 err = t.Time.UnmarshalText([]byte(ti)) 100 t.Valid = valid 101 return err 102 case nil: 103 t.Valid = false 104 return nil 105 default: 106 return fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Time", reflect.TypeOf(v).Name()) 107 } 108 } 109 110 func (t Time) MarshalText() ([]byte, error) { 111 ti := t.Time 112 if !t.Valid { 113 ti = time.Time{} 114 } 115 return ti.MarshalText() 116 } 117 118 func (t *Time) UnmarshalText(text []byte) error { 119 str := string(text) 120 if str == "" || str == "null" { 121 t.Valid = false 122 return nil 123 } 124 if err := t.Time.UnmarshalText(text); err != nil { 125 return err 126 } 127 t.Valid = true 128 return nil 129 } 130 131 // SetValid changes this Time's value and 132 // sets it to be non-null. 133 func (t *Time) SetValid(v time.Time) { 134 t.Time = v 135 t.Valid = true 136 } 137 138 // Ptr returns a pointer to this Time's value, 139 // or a nil pointer if this Time is zero. 140 func (t Time) Ptr() *time.Time { 141 if !t.Valid { 142 return nil 143 } 144 return &t.Time 145 } 146 147 // IsZero returns true for null or zero Times, for potential future omitempty support. 148 func (t Time) IsZero() bool { 149 return !t.Valid || t.Time.IsZero() 150 }