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