1 package pgtype
2
3 import (
4 "database/sql/driver"
5 "encoding/binary"
6 "fmt"
7 "strconv"
8
9 "github.com/jackc/pgx/v5/internal/pgio"
10 )
11
12 type TimeScanner interface {
13 ScanTime(v Time) error
14 }
15
16 type TimeValuer interface {
17 TimeValue() (Time, error)
18 }
19
20
21
22
23
24
25 type Time struct {
26 Microseconds int64
27 Valid bool
28 }
29
30 func (t *Time) ScanTime(v Time) error {
31 *t = v
32 return nil
33 }
34
35 func (t Time) TimeValue() (Time, error) {
36 return t, nil
37 }
38
39
40 func (t *Time) Scan(src any) error {
41 if src == nil {
42 *t = Time{}
43 return nil
44 }
45
46 switch src := src.(type) {
47 case string:
48 return scanPlanTextAnyToTimeScanner{}.Scan([]byte(src), t)
49 }
50
51 return fmt.Errorf("cannot scan %T", src)
52 }
53
54
55 func (t Time) Value() (driver.Value, error) {
56 if !t.Valid {
57 return nil, nil
58 }
59
60 buf, err := TimeCodec{}.PlanEncode(nil, 0, TextFormatCode, t).Encode(t, nil)
61 if err != nil {
62 return nil, err
63 }
64 return string(buf), err
65 }
66
67 type TimeCodec struct{}
68
69 func (TimeCodec) FormatSupported(format int16) bool {
70 return format == TextFormatCode || format == BinaryFormatCode
71 }
72
73 func (TimeCodec) PreferredFormat() int16 {
74 return BinaryFormatCode
75 }
76
77 func (TimeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
78 if _, ok := value.(TimeValuer); !ok {
79 return nil
80 }
81
82 switch format {
83 case BinaryFormatCode:
84 return encodePlanTimeCodecBinary{}
85 case TextFormatCode:
86 return encodePlanTimeCodecText{}
87 }
88
89 return nil
90 }
91
92 type encodePlanTimeCodecBinary struct{}
93
94 func (encodePlanTimeCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
95 t, err := value.(TimeValuer).TimeValue()
96 if err != nil {
97 return nil, err
98 }
99
100 if !t.Valid {
101 return nil, nil
102 }
103
104 return pgio.AppendInt64(buf, t.Microseconds), nil
105 }
106
107 type encodePlanTimeCodecText struct{}
108
109 func (encodePlanTimeCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
110 t, err := value.(TimeValuer).TimeValue()
111 if err != nil {
112 return nil, err
113 }
114
115 if !t.Valid {
116 return nil, nil
117 }
118
119 usec := t.Microseconds
120 hours := usec / microsecondsPerHour
121 usec -= hours * microsecondsPerHour
122 minutes := usec / microsecondsPerMinute
123 usec -= minutes * microsecondsPerMinute
124 seconds := usec / microsecondsPerSecond
125 usec -= seconds * microsecondsPerSecond
126
127 s := fmt.Sprintf("%02d:%02d:%02d.%06d", hours, minutes, seconds, usec)
128
129 return append(buf, s...), nil
130 }
131
132 func (TimeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
133
134 switch format {
135 case BinaryFormatCode:
136 switch target.(type) {
137 case TimeScanner:
138 return scanPlanBinaryTimeToTimeScanner{}
139 }
140 case TextFormatCode:
141 switch target.(type) {
142 case TimeScanner:
143 return scanPlanTextAnyToTimeScanner{}
144 }
145 }
146
147 return nil
148 }
149
150 type scanPlanBinaryTimeToTimeScanner struct{}
151
152 func (scanPlanBinaryTimeToTimeScanner) Scan(src []byte, dst any) error {
153 scanner := (dst).(TimeScanner)
154
155 if src == nil {
156 return scanner.ScanTime(Time{})
157 }
158
159 if len(src) != 8 {
160 return fmt.Errorf("invalid length for time: %v", len(src))
161 }
162
163 usec := int64(binary.BigEndian.Uint64(src))
164
165 return scanner.ScanTime(Time{Microseconds: usec, Valid: true})
166 }
167
168 type scanPlanTextAnyToTimeScanner struct{}
169
170 func (scanPlanTextAnyToTimeScanner) Scan(src []byte, dst any) error {
171 scanner := (dst).(TimeScanner)
172
173 if src == nil {
174 return scanner.ScanTime(Time{})
175 }
176
177 s := string(src)
178
179 if len(s) < 8 {
180 return fmt.Errorf("cannot decode %v into Time", s)
181 }
182
183 hours, err := strconv.ParseInt(s[0:2], 10, 64)
184 if err != nil {
185 return fmt.Errorf("cannot decode %v into Time", s)
186 }
187 usec := hours * microsecondsPerHour
188
189 minutes, err := strconv.ParseInt(s[3:5], 10, 64)
190 if err != nil {
191 return fmt.Errorf("cannot decode %v into Time", s)
192 }
193 usec += minutes * microsecondsPerMinute
194
195 seconds, err := strconv.ParseInt(s[6:8], 10, 64)
196 if err != nil {
197 return fmt.Errorf("cannot decode %v into Time", s)
198 }
199 usec += seconds * microsecondsPerSecond
200
201 if len(s) > 9 {
202 fraction := s[9:]
203 n, err := strconv.ParseInt(fraction, 10, 64)
204 if err != nil {
205 return fmt.Errorf("cannot decode %v into Time", s)
206 }
207
208 for i := len(fraction); i < 6; i++ {
209 n *= 10
210 }
211
212 usec += n
213 }
214
215 return scanner.ScanTime(Time{Microseconds: usec, Valid: true})
216 }
217
218 func (c TimeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
219 return codecDecodeToTextFormat(c, m, oid, format, src)
220 }
221
222 func (c TimeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
223 if src == nil {
224 return nil, nil
225 }
226
227 var t Time
228 err := codecScan(c, m, oid, format, src, &t)
229 if err != nil {
230 return nil, err
231 }
232 return t, nil
233 }
234
View as plain text