1 package pgtype
2
3 import (
4 "database/sql"
5 "database/sql/driver"
6 "encoding/binary"
7 "encoding/json"
8 "fmt"
9 "strings"
10 "time"
11
12 "github.com/jackc/pgio"
13 )
14
15 type Date struct {
16 Time time.Time
17 Status Status
18 InfinityModifier InfinityModifier
19 }
20
21 const (
22 negativeInfinityDayOffset = -2147483648
23 infinityDayOffset = 2147483647
24 )
25
26 func (dst *Date) Set(src interface{}) error {
27 if src == nil {
28 *dst = Date{Status: Null}
29 return nil
30 }
31
32 if value, ok := src.(interface{ Get() interface{} }); ok {
33 value2 := value.Get()
34 if value2 != value {
35 return dst.Set(value2)
36 }
37 }
38
39 if value, ok := src.(interface{ Value() (driver.Value, error) }); ok {
40 v, err := value.Value()
41 if err != nil {
42 return fmt.Errorf("cannot get value %v for Date: %v", value, err)
43 }
44 return dst.Set(v)
45 }
46
47 switch value := src.(type) {
48 case time.Time:
49 *dst = Date{Time: value, Status: Present}
50 case *time.Time:
51 if value == nil {
52 *dst = Date{Status: Null}
53 } else {
54 return dst.Set(*value)
55 }
56 case string:
57 return dst.DecodeText(nil, []byte(value))
58 case *string:
59 if value == nil {
60 *dst = Date{Status: Null}
61 } else {
62 return dst.Set(*value)
63 }
64 default:
65 if originalSrc, ok := underlyingTimeType(src); ok {
66 return dst.Set(originalSrc)
67 }
68 return fmt.Errorf("cannot convert %v to Date", value)
69 }
70
71 return nil
72 }
73
74 func (dst Date) Get() interface{} {
75 switch dst.Status {
76 case Present:
77 if dst.InfinityModifier != None {
78 return dst.InfinityModifier
79 }
80 return dst.Time
81 case Null:
82 return nil
83 default:
84 return dst.Status
85 }
86 }
87
88 func (src *Date) AssignTo(dst interface{}) error {
89 if scanner, ok := dst.(sql.Scanner); ok {
90 var err error
91 switch src.Status {
92 case Present:
93 if src.InfinityModifier != None {
94 err = scanner.Scan(src.InfinityModifier.String())
95 } else {
96 err = scanner.Scan(src.Time)
97 }
98 case Null:
99 err = scanner.Scan(nil)
100 }
101 if err != nil {
102 return fmt.Errorf("unable assign %v to %T: %s", src, dst, err)
103 }
104 return nil
105 }
106
107 switch src.Status {
108 case Present:
109 switch v := dst.(type) {
110 case *time.Time:
111 if src.InfinityModifier != None {
112 return fmt.Errorf("cannot assign %v to %T", src, dst)
113 }
114 *v = src.Time
115 return nil
116 default:
117 if nextDst, retry := GetAssignToDstType(dst); retry {
118 return src.AssignTo(nextDst)
119 }
120 return fmt.Errorf("unable to assign to %T", dst)
121 }
122 case Null:
123 return NullAssignTo(dst)
124 }
125
126 return fmt.Errorf("cannot decode %#v into %T", src, dst)
127 }
128
129 func (dst *Date) DecodeText(ci *ConnInfo, src []byte) error {
130 if src == nil {
131 *dst = Date{Status: Null}
132 return nil
133 }
134
135 sbuf := string(src)
136 switch sbuf {
137 case "infinity":
138 *dst = Date{Status: Present, InfinityModifier: Infinity}
139 case "-infinity":
140 *dst = Date{Status: Present, InfinityModifier: -Infinity}
141 default:
142 if strings.HasSuffix(sbuf, " BC") {
143 t, err := time.ParseInLocation("2006-01-02", strings.TrimRight(sbuf, " BC"), time.UTC)
144 t2 := time.Date(1-t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
145 if err != nil {
146 return err
147 }
148 *dst = Date{Time: t2, Status: Present}
149 return nil
150 }
151 t, err := time.ParseInLocation("2006-01-02", sbuf, time.UTC)
152 if err != nil {
153 return err
154 }
155
156 *dst = Date{Time: t, Status: Present}
157 }
158
159 return nil
160 }
161
162 func (dst *Date) DecodeBinary(ci *ConnInfo, src []byte) error {
163 if src == nil {
164 *dst = Date{Status: Null}
165 return nil
166 }
167
168 if len(src) != 4 {
169 return fmt.Errorf("invalid length for date: %v", len(src))
170 }
171
172 dayOffset := int32(binary.BigEndian.Uint32(src))
173
174 switch dayOffset {
175 case infinityDayOffset:
176 *dst = Date{Status: Present, InfinityModifier: Infinity}
177 case negativeInfinityDayOffset:
178 *dst = Date{Status: Present, InfinityModifier: -Infinity}
179 default:
180 t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC)
181 *dst = Date{Time: t, Status: Present}
182 }
183
184 return nil
185 }
186
187 func (src Date) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
188 switch src.Status {
189 case Null:
190 return nil, nil
191 case Undefined:
192 return nil, errUndefined
193 }
194
195 var s string
196
197 switch src.InfinityModifier {
198 case None:
199 s = src.Time.Format("2006-01-02")
200 case Infinity:
201 s = "infinity"
202 case NegativeInfinity:
203 s = "-infinity"
204 }
205
206 return append(buf, s...), nil
207 }
208
209 func (src Date) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
210 switch src.Status {
211 case Null:
212 return nil, nil
213 case Undefined:
214 return nil, errUndefined
215 }
216
217 var daysSinceDateEpoch int32
218 switch src.InfinityModifier {
219 case None:
220 tUnix := time.Date(src.Time.Year(), src.Time.Month(), src.Time.Day(), 0, 0, 0, 0, time.UTC).Unix()
221 dateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
222
223 secSinceDateEpoch := tUnix - dateEpoch
224 daysSinceDateEpoch = int32(secSinceDateEpoch / 86400)
225 case Infinity:
226 daysSinceDateEpoch = infinityDayOffset
227 case NegativeInfinity:
228 daysSinceDateEpoch = negativeInfinityDayOffset
229 }
230
231 return pgio.AppendInt32(buf, daysSinceDateEpoch), nil
232 }
233
234
235 func (dst *Date) Scan(src interface{}) error {
236 if src == nil {
237 *dst = Date{Status: Null}
238 return nil
239 }
240
241 switch src := src.(type) {
242 case string:
243 return dst.DecodeText(nil, []byte(src))
244 case []byte:
245 srcCopy := make([]byte, len(src))
246 copy(srcCopy, src)
247 return dst.DecodeText(nil, srcCopy)
248 case time.Time:
249 *dst = Date{Time: src, Status: Present}
250 return nil
251 }
252
253 return fmt.Errorf("cannot scan %T", src)
254 }
255
256
257 func (src Date) Value() (driver.Value, error) {
258 switch src.Status {
259 case Present:
260 if src.InfinityModifier != None {
261 return src.InfinityModifier.String(), nil
262 }
263 return src.Time, nil
264 case Null:
265 return nil, nil
266 default:
267 return nil, errUndefined
268 }
269 }
270
271 func (src Date) MarshalJSON() ([]byte, error) {
272 switch src.Status {
273 case Null:
274 return []byte("null"), nil
275 case Undefined:
276 return nil, errUndefined
277 }
278
279 if src.Status != Present {
280 return nil, errBadStatus
281 }
282
283 var s string
284
285 switch src.InfinityModifier {
286 case None:
287 s = src.Time.Format("2006-01-02")
288 case Infinity:
289 s = "infinity"
290 case NegativeInfinity:
291 s = "-infinity"
292 }
293
294 return json.Marshal(s)
295 }
296
297 func (dst *Date) UnmarshalJSON(b []byte) error {
298 var s *string
299 err := json.Unmarshal(b, &s)
300 if err != nil {
301 return err
302 }
303
304 if s == nil {
305 *dst = Date{Status: Null}
306 return nil
307 }
308
309 switch *s {
310 case "infinity":
311 *dst = Date{Status: Present, InfinityModifier: Infinity}
312 case "-infinity":
313 *dst = Date{Status: Present, InfinityModifier: -Infinity}
314 default:
315 t, err := time.ParseInLocation("2006-01-02", *s, time.UTC)
316 if err != nil {
317 return err
318 }
319
320 *dst = Date{Time: t, Status: Present}
321 }
322
323 return nil
324 }
325
View as plain text