1 package pgtype
2
3 import (
4 "database/sql/driver"
5 "encoding/binary"
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/jackc/pgio"
11 )
12
13 const pgTimestampFormat = "2006-01-02 15:04:05.999999999"
14
15
16
17
18
19
20 type Timestamp struct {
21 Time time.Time
22 Status Status
23 InfinityModifier InfinityModifier
24 }
25
26
27
28 func (dst *Timestamp) Set(src interface{}) error {
29 if src == nil {
30 *dst = Timestamp{Status: Null}
31 return nil
32 }
33
34 if value, ok := src.(interface{ Get() interface{} }); ok {
35 value2 := value.Get()
36 if value2 != value {
37 return dst.Set(value2)
38 }
39 }
40
41 switch value := src.(type) {
42 case time.Time:
43 *dst = Timestamp{Time: time.Date(value.Year(), value.Month(), value.Day(), value.Hour(), value.Minute(), value.Second(), value.Nanosecond(), time.UTC), Status: Present}
44 case *time.Time:
45 if value == nil {
46 *dst = Timestamp{Status: Null}
47 } else {
48 return dst.Set(*value)
49 }
50 case string:
51 return dst.DecodeText(nil, []byte(value))
52 case *string:
53 if value == nil {
54 *dst = Timestamp{Status: Null}
55 } else {
56 return dst.Set(*value)
57 }
58 case InfinityModifier:
59 *dst = Timestamp{InfinityModifier: value, Status: Present}
60 default:
61 if originalSrc, ok := underlyingTimeType(src); ok {
62 return dst.Set(originalSrc)
63 }
64 return fmt.Errorf("cannot convert %v to Timestamp", value)
65 }
66
67 return nil
68 }
69
70 func (dst Timestamp) Get() interface{} {
71 switch dst.Status {
72 case Present:
73 if dst.InfinityModifier != None {
74 return dst.InfinityModifier
75 }
76 return dst.Time
77 case Null:
78 return nil
79 default:
80 return dst.Status
81 }
82 }
83
84 func (src *Timestamp) AssignTo(dst interface{}) error {
85 switch src.Status {
86 case Present:
87 switch v := dst.(type) {
88 case *time.Time:
89 if src.InfinityModifier != None {
90 return fmt.Errorf("cannot assign %v to %T", src, dst)
91 }
92 *v = src.Time
93 return nil
94 default:
95 if nextDst, retry := GetAssignToDstType(dst); retry {
96 return src.AssignTo(nextDst)
97 }
98 return fmt.Errorf("unable to assign to %T", dst)
99 }
100 case Null:
101 return NullAssignTo(dst)
102 }
103
104 return fmt.Errorf("cannot decode %#v into %T", src, dst)
105 }
106
107
108
109 func (dst *Timestamp) DecodeText(ci *ConnInfo, src []byte) error {
110 if src == nil {
111 *dst = Timestamp{Status: Null}
112 return nil
113 }
114
115 sbuf := string(src)
116 switch sbuf {
117 case "infinity":
118 *dst = Timestamp{Status: Present, InfinityModifier: Infinity}
119 case "-infinity":
120 *dst = Timestamp{Status: Present, InfinityModifier: -Infinity}
121 default:
122 if strings.HasSuffix(sbuf, " BC") {
123 t, err := time.Parse(pgTimestampFormat, strings.TrimRight(sbuf, " BC"))
124 t2 := time.Date(1-t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
125 if err != nil {
126 return err
127 }
128 *dst = Timestamp{Time: t2, Status: Present}
129 return nil
130 }
131 tim, err := time.Parse(pgTimestampFormat, sbuf)
132 if err != nil {
133 return err
134 }
135
136 *dst = Timestamp{Time: tim, Status: Present}
137 }
138
139 return nil
140 }
141
142
143
144 func (dst *Timestamp) DecodeBinary(ci *ConnInfo, src []byte) error {
145 if src == nil {
146 *dst = Timestamp{Status: Null}
147 return nil
148 }
149
150 if len(src) != 8 {
151 return fmt.Errorf("invalid length for timestamp: %v", len(src))
152 }
153
154 microsecSinceY2K := int64(binary.BigEndian.Uint64(src))
155
156 switch microsecSinceY2K {
157 case infinityMicrosecondOffset:
158 *dst = Timestamp{Status: Present, InfinityModifier: Infinity}
159 case negativeInfinityMicrosecondOffset:
160 *dst = Timestamp{Status: Present, InfinityModifier: -Infinity}
161 default:
162 tim := time.Unix(
163 microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000,
164 (microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000),
165 ).UTC()
166 *dst = Timestamp{Time: tim, Status: Present}
167 }
168
169 return nil
170 }
171
172
173
174 func (src Timestamp) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
175 switch src.Status {
176 case Null:
177 return nil, nil
178 case Undefined:
179 return nil, errUndefined
180 }
181 if src.Time.Location() != time.UTC {
182 return nil, fmt.Errorf("cannot encode non-UTC time into timestamp")
183 }
184
185 var s string
186
187 switch src.InfinityModifier {
188 case None:
189 s = src.Time.Truncate(time.Microsecond).Format(pgTimestampFormat)
190 case Infinity:
191 s = "infinity"
192 case NegativeInfinity:
193 s = "-infinity"
194 }
195
196 return append(buf, s...), nil
197 }
198
199
200
201 func (src Timestamp) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
202 switch src.Status {
203 case Null:
204 return nil, nil
205 case Undefined:
206 return nil, errUndefined
207 }
208 if src.Time.Location() != time.UTC {
209 return nil, fmt.Errorf("cannot encode non-UTC time into timestamp")
210 }
211
212 var microsecSinceY2K int64
213 switch src.InfinityModifier {
214 case None:
215 microsecSinceUnixEpoch := src.Time.Unix()*1000000 + int64(src.Time.Nanosecond())/1000
216 microsecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K
217 case Infinity:
218 microsecSinceY2K = infinityMicrosecondOffset
219 case NegativeInfinity:
220 microsecSinceY2K = negativeInfinityMicrosecondOffset
221 }
222
223 return pgio.AppendInt64(buf, microsecSinceY2K), nil
224 }
225
226
227 func (dst *Timestamp) Scan(src interface{}) error {
228 if src == nil {
229 *dst = Timestamp{Status: Null}
230 return nil
231 }
232
233 switch src := src.(type) {
234 case string:
235 return dst.DecodeText(nil, []byte(src))
236 case []byte:
237 srcCopy := make([]byte, len(src))
238 copy(srcCopy, src)
239 return dst.DecodeText(nil, srcCopy)
240 case time.Time:
241 *dst = Timestamp{Time: src, Status: Present}
242 return nil
243 }
244
245 return fmt.Errorf("cannot scan %T", src)
246 }
247
248
249 func (src Timestamp) Value() (driver.Value, error) {
250 switch src.Status {
251 case Present:
252 if src.InfinityModifier != None {
253 return src.InfinityModifier.String(), nil
254 }
255 return src.Time, nil
256 case Null:
257 return nil, nil
258 default:
259 return nil, errUndefined
260 }
261 }
262
View as plain text