Commit 07daaf4e563562e18240031d27d8936d45d0af2d
1 parent
6a5c6931eb
Exists in
master
separate nullable time, date and timedate structs
Showing
2 changed files
with
224 additions
and
13 deletions
Show diff stats
nullables.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "database/sql" | 4 | "database/sql" |
5 | "database/sql/driver" | 5 | "database/sql/driver" |
6 | "encoding/json" | 6 | "encoding/json" |
7 | "fmt" | 7 | "fmt" |
8 | "time" | 8 | "time" |
9 | ) | 9 | ) |
10 | 10 | ||
11 | // NullBool is a wrapper for sql.NullBool with added JSON (un)marshalling | 11 | // NullBool is a wrapper for sql.NullBool with added JSON (un)marshalling |
12 | type NullBool sql.NullBool | 12 | type NullBool sql.NullBool |
13 | 13 | ||
14 | // Scan ... | 14 | // Scan ... |
15 | func (nb *NullBool) Scan(value interface{}) error { | 15 | func (nb *NullBool) Scan(value interface{}) error { |
16 | var b sql.NullBool | 16 | var b sql.NullBool |
17 | if err := b.Scan(value); err != nil { | 17 | if err := b.Scan(value); err != nil { |
18 | nb.Bool, nb.Valid = false, false | 18 | nb.Bool, nb.Valid = false, false |
19 | return err | 19 | return err |
20 | } | 20 | } |
21 | nb.Bool, nb.Valid = b.Bool, b.Valid | 21 | nb.Bool, nb.Valid = b.Bool, b.Valid |
22 | return nil | 22 | return nil |
23 | } | 23 | } |
24 | 24 | ||
25 | // Value ... | 25 | // Value ... |
26 | func (nb *NullBool) Value() (driver.Value, error) { | 26 | func (nb *NullBool) Value() (driver.Value, error) { |
27 | if !nb.Valid { | 27 | if !nb.Valid { |
28 | return nil, nil | 28 | return nil, nil |
29 | } | 29 | } |
30 | return nb.Bool, nil | 30 | return nb.Bool, nil |
31 | } | 31 | } |
32 | 32 | ||
33 | // MarshalJSON ... | 33 | // MarshalJSON ... |
34 | func (nb NullBool) MarshalJSON() ([]byte, error) { | 34 | func (nb NullBool) MarshalJSON() ([]byte, error) { |
35 | if nb.Valid { | 35 | if nb.Valid { |
36 | return json.Marshal(nb.Bool) | 36 | return json.Marshal(nb.Bool) |
37 | } | 37 | } |
38 | 38 | ||
39 | return json.Marshal(nil) | 39 | return json.Marshal(nil) |
40 | } | 40 | } |
41 | 41 | ||
42 | // UnmarshalJSON ... | 42 | // UnmarshalJSON ... |
43 | func (nb *NullBool) UnmarshalJSON(b []byte) error { | 43 | func (nb *NullBool) UnmarshalJSON(b []byte) error { |
44 | var temp *bool | 44 | var temp *bool |
45 | if err := json.Unmarshal(b, &temp); err != nil { | 45 | if err := json.Unmarshal(b, &temp); err != nil { |
46 | return err | 46 | return err |
47 | } | 47 | } |
48 | if temp != nil { | 48 | if temp != nil { |
49 | nb.Valid = true | 49 | nb.Valid = true |
50 | nb.Bool = *temp | 50 | nb.Bool = *temp |
51 | } else { | 51 | } else { |
52 | nb.Valid = false | 52 | nb.Valid = false |
53 | } | 53 | } |
54 | return nil | 54 | return nil |
55 | } | 55 | } |
56 | 56 | ||
57 | // SQLCast ... | 57 | // CastToSQL ... |
58 | func (nb *NullBool) SQLCast() sql.NullBool { | 58 | func (nb *NullBool) CastToSQL() sql.NullBool { |
59 | return sql.NullBool(*nb) | 59 | return sql.NullBool(*nb) |
60 | } | 60 | } |
61 | 61 | ||
62 | // NullString is a wrapper for sql.NullString with added JSON (un)marshalling | 62 | // NullString is a wrapper for sql.NullString with added JSON (un)marshalling |
63 | type NullString sql.NullString | 63 | type NullString sql.NullString |
64 | 64 | ||
65 | // Scan ... | 65 | // Scan ... |
66 | func (ns *NullString) Scan(value interface{}) error { | 66 | func (ns *NullString) Scan(value interface{}) error { |
67 | var s sql.NullString | 67 | var s sql.NullString |
68 | if err := s.Scan(value); err != nil { | 68 | if err := s.Scan(value); err != nil { |
69 | ns.String, ns.Valid = "", false | 69 | ns.String, ns.Valid = "", false |
70 | return err | 70 | return err |
71 | } | 71 | } |
72 | ns.String, ns.Valid = s.String, s.Valid | 72 | ns.String, ns.Valid = s.String, s.Valid |
73 | return nil | 73 | return nil |
74 | } | 74 | } |
75 | 75 | ||
76 | // Value ... | 76 | // Value ... |
77 | func (ns *NullString) Value() (driver.Value, error) { | 77 | func (ns *NullString) Value() (driver.Value, error) { |
78 | if !ns.Valid { | 78 | if !ns.Valid { |
79 | return nil, nil | 79 | return nil, nil |
80 | } | 80 | } |
81 | return ns.String, nil | 81 | return ns.String, nil |
82 | } | 82 | } |
83 | 83 | ||
84 | // MarshalJSON ... | 84 | // MarshalJSON ... |
85 | func (ns NullString) MarshalJSON() ([]byte, error) { | 85 | func (ns NullString) MarshalJSON() ([]byte, error) { |
86 | if ns.Valid { | 86 | if ns.Valid { |
87 | return json.Marshal(ns.String) | 87 | return json.Marshal(ns.String) |
88 | } | 88 | } |
89 | return json.Marshal(nil) | 89 | return json.Marshal(nil) |
90 | } | 90 | } |
91 | 91 | ||
92 | // UnmarshalJSON ... | 92 | // UnmarshalJSON ... |
93 | func (ns *NullString) UnmarshalJSON(b []byte) error { | 93 | func (ns *NullString) UnmarshalJSON(b []byte) error { |
94 | var temp *string | 94 | var temp *string |
95 | if err := json.Unmarshal(b, &temp); err != nil { | 95 | if err := json.Unmarshal(b, &temp); err != nil { |
96 | return err | 96 | return err |
97 | } | 97 | } |
98 | if temp != nil { | 98 | if temp != nil { |
99 | ns.Valid = true | 99 | ns.Valid = true |
100 | ns.String = *temp | 100 | ns.String = *temp |
101 | } else { | 101 | } else { |
102 | ns.Valid = false | 102 | ns.Valid = false |
103 | } | 103 | } |
104 | return nil | 104 | return nil |
105 | } | 105 | } |
106 | 106 | ||
107 | // SQLCast ... | 107 | // CastToSQL ... |
108 | func (ns *NullString) SQLCast() sql.NullString { | 108 | func (ns *NullString) CastToSQL() sql.NullString { |
109 | return sql.NullString(*ns) | 109 | return sql.NullString(*ns) |
110 | } | 110 | } |
111 | 111 | ||
112 | // NullInt64 is a wrapper for sql.NullInt64 with added JSON (un)marshalling | 112 | // NullInt64 is a wrapper for sql.NullInt64 with added JSON (un)marshalling |
113 | type NullInt64 sql.NullInt64 | 113 | type NullInt64 sql.NullInt64 |
114 | 114 | ||
115 | // Scan ... | 115 | // Scan ... |
116 | func (ni *NullInt64) Scan(value interface{}) error { | 116 | func (ni *NullInt64) Scan(value interface{}) error { |
117 | var i sql.NullInt64 | 117 | var i sql.NullInt64 |
118 | if err := i.Scan(value); err != nil { | 118 | if err := i.Scan(value); err != nil { |
119 | ni.Int64, ni.Valid = 0, false | 119 | ni.Int64, ni.Valid = 0, false |
120 | return err | 120 | return err |
121 | } | 121 | } |
122 | ni.Int64, ni.Valid = i.Int64, i.Valid | 122 | ni.Int64, ni.Valid = i.Int64, i.Valid |
123 | return nil | 123 | return nil |
124 | } | 124 | } |
125 | 125 | ||
126 | // ScanPtr ... | 126 | // ScanPtr ... |
127 | func (ni *NullInt64) ScanPtr(v interface{}) error { | 127 | func (ni *NullInt64) ScanPtr(v interface{}) error { |
128 | if ip, ok := v.(*int64); ok && ip != nil { | 128 | if ip, ok := v.(*int64); ok && ip != nil { |
129 | return ni.Scan(*ip) | 129 | return ni.Scan(*ip) |
130 | } | 130 | } |
131 | return nil | 131 | return nil |
132 | } | 132 | } |
133 | 133 | ||
134 | // Value ... | 134 | // Value ... |
135 | func (ni *NullInt64) Value() (driver.Value, error) { | 135 | func (ni *NullInt64) Value() (driver.Value, error) { |
136 | if !ni.Valid { | 136 | if !ni.Valid { |
137 | return nil, nil | 137 | return nil, nil |
138 | } | 138 | } |
139 | return ni.Int64, nil | 139 | return ni.Int64, nil |
140 | } | 140 | } |
141 | 141 | ||
142 | func (ni *NullInt64) Val() int64 { | 142 | func (ni *NullInt64) Val() int64 { |
143 | return ni.Int64 | 143 | return ni.Int64 |
144 | } | 144 | } |
145 | 145 | ||
146 | // Add | 146 | // Add |
147 | func (ni *NullInt64) Add(i NullInt64) { | 147 | func (ni *NullInt64) Add(i NullInt64) { |
148 | ni.Valid = true | 148 | ni.Valid = true |
149 | ni.Int64 += i.Int64 | 149 | ni.Int64 += i.Int64 |
150 | } | 150 | } |
151 | 151 | ||
152 | func (ni *NullInt64) Set(i int64) { | 152 | func (ni *NullInt64) Set(i int64) { |
153 | ni.Valid = true | 153 | ni.Valid = true |
154 | ni.Int64 = i | 154 | ni.Int64 = i |
155 | } | 155 | } |
156 | 156 | ||
157 | // MarshalJSON ... | 157 | // MarshalJSON ... |
158 | func (ni NullInt64) MarshalJSON() ([]byte, error) { | 158 | func (ni NullInt64) MarshalJSON() ([]byte, error) { |
159 | if ni.Valid { | 159 | if ni.Valid { |
160 | return json.Marshal(ni.Int64) | 160 | return json.Marshal(ni.Int64) |
161 | } | 161 | } |
162 | return json.Marshal(nil) | 162 | return json.Marshal(nil) |
163 | } | 163 | } |
164 | 164 | ||
165 | // UnmarshalJSON ... | 165 | // UnmarshalJSON ... |
166 | func (ni *NullInt64) UnmarshalJSON(b []byte) error { | 166 | func (ni *NullInt64) UnmarshalJSON(b []byte) error { |
167 | var temp *int64 | 167 | var temp *int64 |
168 | if err := json.Unmarshal(b, &temp); err != nil { | 168 | if err := json.Unmarshal(b, &temp); err != nil { |
169 | return err | 169 | return err |
170 | } | 170 | } |
171 | if temp != nil { | 171 | if temp != nil { |
172 | ni.Valid = true | 172 | ni.Valid = true |
173 | ni.Int64 = *temp | 173 | ni.Int64 = *temp |
174 | } else { | 174 | } else { |
175 | ni.Valid = false | 175 | ni.Valid = false |
176 | } | 176 | } |
177 | return nil | 177 | return nil |
178 | } | 178 | } |
179 | 179 | ||
180 | // SQLCast ... | 180 | // CastToSQL ... |
181 | func (ni *NullInt64) SQLCast() sql.NullInt64 { | 181 | func (ni *NullInt64) CastToSQL() sql.NullInt64 { |
182 | return sql.NullInt64(*ni) | 182 | return sql.NullInt64(*ni) |
183 | } | 183 | } |
184 | 184 | ||
185 | // NullFloat64 is a wrapper for sql.NullFloat64 with added JSON (un)marshalling | 185 | // NullFloat64 is a wrapper for sql.NullFloat64 with added JSON (un)marshalling |
186 | type NullFloat64 sql.NullFloat64 | 186 | type NullFloat64 sql.NullFloat64 |
187 | 187 | ||
188 | // Scan ... | 188 | // Scan ... |
189 | func (nf *NullFloat64) Scan(value interface{}) error { | 189 | func (nf *NullFloat64) Scan(value interface{}) error { |
190 | var f sql.NullFloat64 | 190 | var f sql.NullFloat64 |
191 | if err := f.Scan(value); err != nil { | 191 | if err := f.Scan(value); err != nil { |
192 | nf.Float64, nf.Valid = 0.0, false | 192 | nf.Float64, nf.Valid = 0.0, false |
193 | return err | 193 | return err |
194 | } | 194 | } |
195 | nf.Float64, nf.Valid = f.Float64, f.Valid | 195 | nf.Float64, nf.Valid = f.Float64, f.Valid |
196 | return nil | 196 | return nil |
197 | } | 197 | } |
198 | 198 | ||
199 | // ScanPtr ... | 199 | // ScanPtr ... |
200 | func (nf *NullFloat64) ScanPtr(v interface{}) error { | 200 | func (nf *NullFloat64) ScanPtr(v interface{}) error { |
201 | if fp, ok := v.(*float64); ok && fp != nil { | 201 | if fp, ok := v.(*float64); ok && fp != nil { |
202 | return nf.Scan(*fp) | 202 | return nf.Scan(*fp) |
203 | } | 203 | } |
204 | return nil | 204 | return nil |
205 | } | 205 | } |
206 | 206 | ||
207 | // Value ... | 207 | // Value ... |
208 | func (nf *NullFloat64) Value() (driver.Value, error) { | 208 | func (nf *NullFloat64) Value() (driver.Value, error) { |
209 | if !nf.Valid { | 209 | if !nf.Valid { |
210 | return nil, nil | 210 | return nil, nil |
211 | } | 211 | } |
212 | return nf.Float64, nil | 212 | return nf.Float64, nil |
213 | } | 213 | } |
214 | 214 | ||
215 | // Val ... | 215 | // Val ... |
216 | func (nf *NullFloat64) Val() float64 { | 216 | func (nf *NullFloat64) Val() float64 { |
217 | return nf.Float64 | 217 | return nf.Float64 |
218 | } | 218 | } |
219 | 219 | ||
220 | // Add ... | 220 | // Add ... |
221 | func (nf *NullFloat64) Add(f NullFloat64) { | 221 | func (nf *NullFloat64) Add(f NullFloat64) { |
222 | nf.Valid = true | 222 | nf.Valid = true |
223 | nf.Float64 += f.Float64 | 223 | nf.Float64 += f.Float64 |
224 | } | 224 | } |
225 | 225 | ||
226 | func (nf *NullFloat64) Set(f float64) { | 226 | func (nf *NullFloat64) Set(f float64) { |
227 | nf.Valid = true | 227 | nf.Valid = true |
228 | nf.Float64 = f | 228 | nf.Float64 = f |
229 | } | 229 | } |
230 | 230 | ||
231 | // MarshalJSON ... | 231 | // MarshalJSON ... |
232 | func (nf NullFloat64) MarshalJSON() ([]byte, error) { | 232 | func (nf NullFloat64) MarshalJSON() ([]byte, error) { |
233 | if nf.Valid { | 233 | if nf.Valid { |
234 | return json.Marshal(nf.Float64) | 234 | return json.Marshal(nf.Float64) |
235 | } | 235 | } |
236 | return json.Marshal(nil) | 236 | return json.Marshal(nil) |
237 | } | 237 | } |
238 | 238 | ||
239 | // UnmarshalJSON ... | 239 | // UnmarshalJSON ... |
240 | func (nf *NullFloat64) UnmarshalJSON(b []byte) error { | 240 | func (nf *NullFloat64) UnmarshalJSON(b []byte) error { |
241 | var temp *float64 | 241 | var temp *float64 |
242 | if err := json.Unmarshal(b, &temp); err != nil { | 242 | if err := json.Unmarshal(b, &temp); err != nil { |
243 | return err | 243 | return err |
244 | } | 244 | } |
245 | if temp != nil { | 245 | if temp != nil { |
246 | nf.Valid = true | 246 | nf.Valid = true |
247 | nf.Float64 = *temp | 247 | nf.Float64 = *temp |
248 | } else { | 248 | } else { |
249 | nf.Valid = false | 249 | nf.Valid = false |
250 | } | 250 | } |
251 | return nil | 251 | return nil |
252 | } | 252 | } |
253 | 253 | ||
254 | // SQLCast ... | 254 | // CastToSQL ... |
255 | func (nf *NullFloat64) SQLCast() sql.NullFloat64 { | 255 | func (nf *NullFloat64) CastToSQL() sql.NullFloat64 { |
256 | return sql.NullFloat64(*nf) | 256 | return sql.NullFloat64(*nf) |
257 | } | 257 | } |
258 | 258 | ||
259 | // NullDateTime ... | ||
260 | type NullDateTime struct { | ||
261 | Time time.Time | ||
262 | Valid bool // Valid is true if Time is not NULL | ||
263 | } | ||
264 | |||
265 | // Scan ... | ||
266 | func (nt *NullDateTime) Scan(value interface{}) (err error) { | ||
267 | if value == nil { | ||
268 | nt.Time, nt.Valid = time.Time{}, false | ||
269 | return | ||
270 | } | ||
271 | |||
272 | switch v := value.(type) { | ||
273 | case time.Time: | ||
274 | nt.Time, nt.Valid = v, true | ||
275 | return | ||
276 | case []byte: | ||
277 | nt.Time, err = parseSQLDateTime(string(v), time.UTC) | ||
278 | nt.Valid = (err == nil) | ||
279 | return | ||
280 | case string: | ||
281 | nt.Time, err = parseSQLDateTime(v, time.UTC) | ||
282 | nt.Valid = (err == nil) | ||
283 | return | ||
284 | } | ||
285 | |||
286 | nt.Valid = false | ||
287 | return fmt.Errorf("Can't convert %T to time.Time", value) | ||
288 | } | ||
289 | |||
290 | // Value implements the driver Valuer interface. | ||
291 | func (nt NullDateTime) Value() (driver.Value, error) { | ||
292 | if !nt.Valid { | ||
293 | return nil, nil | ||
294 | } | ||
295 | return nt.Time, nil | ||
296 | } | ||
297 | |||
298 | // MarshalJSON ... | ||
299 | func (nt NullDateTime) MarshalJSON() ([]byte, error) { | ||
300 | if nt.Valid { | ||
301 | format := nt.Time.Format("2006-01-02 15:04:05") | ||
302 | return json.Marshal(format) | ||
303 | } | ||
304 | return json.Marshal(nil) | ||
305 | } | ||
306 | |||
307 | // UnmarshalJSON ... | ||
308 | func (nt *NullDateTime) UnmarshalJSON(b []byte) error { | ||
309 | var temp *time.Time | ||
310 | var t1 time.Time | ||
311 | var err error | ||
312 | |||
313 | s1 := string(b) | ||
314 | s2 := s1[1 : len(s1)-1] | ||
315 | if s1 == "null" { | ||
316 | temp = nil | ||
317 | } else { | ||
318 | t1, err = time.Parse("2006-01-02 15:04:05", s2) | ||
319 | if err != nil { | ||
320 | return err | ||
321 | } | ||
322 | temp = &t1 | ||
323 | } | ||
324 | |||
325 | if temp != nil { | ||
326 | nt.Valid = true | ||
327 | nt.Time = *temp | ||
328 | } else { | ||
329 | nt.Valid = false | ||
330 | } | ||
331 | return nil | ||
332 | } | ||
333 | |||
334 | func (nt *NullDateTime) CastToSQL() NullDateTime { | ||
335 | return *nt | ||
336 | } | ||
337 | |||
338 | // NullDate ... | ||
339 | type NullDate struct { | ||
340 | Time time.Time | ||
341 | Valid bool // Valid is true if Time is not NULL | ||
342 | } | ||
343 | |||
344 | // Scan ... | ||
345 | func (nt *NullDate) Scan(value interface{}) (err error) { | ||
346 | if value == nil { | ||
347 | nt.Time, nt.Valid = time.Time{}, false | ||
348 | return | ||
349 | } | ||
350 | |||
351 | switch v := value.(type) { | ||
352 | case time.Time: | ||
353 | nt.Time, nt.Valid = v, true | ||
354 | return | ||
355 | case []byte: | ||
356 | nt.Time, err = parseSQLDate(string(v), time.UTC) | ||
357 | nt.Valid = (err == nil) | ||
358 | return | ||
359 | case string: | ||
360 | nt.Time, err = parseSQLDate(v, time.UTC) | ||
361 | nt.Valid = (err == nil) | ||
362 | return | ||
363 | } | ||
364 | |||
365 | nt.Valid = false | ||
366 | return fmt.Errorf("Can't convert %T to time.Time", value) | ||
367 | } | ||
368 | |||
369 | // Value implements the driver Valuer interface. | ||
370 | func (nt NullDate) Value() (driver.Value, error) { | ||
371 | if !nt.Valid { | ||
372 | return nil, nil | ||
373 | } | ||
374 | return nt.Time, nil | ||
375 | } | ||
376 | |||
377 | // MarshalJSON ... | ||
378 | func (nt NullDate) MarshalJSON() ([]byte, error) { | ||
379 | if nt.Valid { | ||
380 | format := nt.Time.Format("2006-01-02") | ||
381 | return json.Marshal(format) | ||
382 | } | ||
383 | return json.Marshal(nil) | ||
384 | } | ||
385 | |||
386 | // UnmarshalJSON ... | ||
387 | func (nt *NullDate) UnmarshalJSON(b []byte) error { | ||
388 | var temp *time.Time | ||
389 | var t1 time.Time | ||
390 | var err error | ||
391 | |||
392 | s1 := string(b) | ||
393 | s2 := s1[1 : len(s1)-1] | ||
394 | if s1 == "null" { | ||
395 | temp = nil | ||
396 | } else { | ||
397 | t1, err = time.Parse("2006-01-02", s2) | ||
398 | if err != nil { | ||
399 | return err | ||
400 | } | ||
401 | temp = &t1 | ||
402 | } | ||
403 | |||
404 | if temp != nil { | ||
405 | nt.Valid = true | ||
406 | nt.Time = *temp | ||
407 | } else { | ||
408 | nt.Valid = false | ||
409 | } | ||
410 | return nil | ||
411 | } | ||
412 | |||
413 | func (nt *NullDate) CastToSQL() NullDate { | ||
414 | return *nt | ||
415 | } | ||
416 | |||
259 | // NullTime ... | 417 | // NullTime ... |
260 | type NullTime struct { | 418 | type NullTime struct { |
261 | Time time.Time | 419 | Time time.Time |
262 | Valid bool // Valid is true if Time is not NULL | 420 | Valid bool // Valid is true if Time is not NULL |
263 | } | 421 | } |
264 | 422 | ||
265 | // Scan ... | 423 | // Scan ... |
266 | func (nt *NullTime) Scan(value interface{}) (err error) { | 424 | func (nt *NullTime) Scan(value interface{}) (err error) { |
267 | if value == nil { | 425 | if value == nil { |
268 | nt.Time, nt.Valid = time.Time{}, false | 426 | nt.Time, nt.Valid = time.Time{}, false |
269 | return | 427 | return |
270 | } | 428 | } |
271 | 429 | ||
272 | switch v := value.(type) { | 430 | switch v := value.(type) { |
273 | case time.Time: | 431 | case time.Time: |
274 | nt.Time, nt.Valid = v, true | 432 | nt.Time, nt.Valid = v, true |
275 | return | 433 | return |
276 | case []byte: | 434 | case []byte: |
277 | nt.Time, err = parseDateTime(string(v), time.UTC) | 435 | nt.Time, err = parseSQLTime(string(v), time.UTC) |
278 | nt.Valid = (err == nil) | 436 | nt.Valid = (err == nil) |
279 | return | 437 | return |
280 | case string: | 438 | case string: |
281 | nt.Time, err = parseDateTime(v, time.UTC) | 439 | nt.Time, err = parseSQLTime(v, time.UTC) |
282 | nt.Valid = (err == nil) | 440 | nt.Valid = (err == nil) |
283 | return | 441 | return |
284 | } | 442 | } |
285 | 443 | ||
286 | nt.Valid = false | 444 | nt.Valid = false |
287 | return fmt.Errorf("Can't convert %T to time.Time", value) | 445 | return fmt.Errorf("Can't convert %T to time.Time", value) |
288 | } | 446 | } |
289 | 447 | ||
290 | // Value implements the driver Valuer interface. | 448 | // Value implements the driver Valuer interface. |
291 | func (nt NullTime) Value() (driver.Value, error) { | 449 | func (nt NullTime) Value() (driver.Value, error) { |
292 | if !nt.Valid { | 450 | if !nt.Valid { |
293 | return nil, nil | 451 | return nil, nil |
294 | } | 452 | } |
295 | return nt.Time, nil | 453 | return nt.Time, nil |
296 | } | 454 | } |
297 | 455 | ||
298 | // MarshalJSON ... | 456 | // MarshalJSON ... |
299 | func (nt NullTime) MarshalJSON() ([]byte, error) { | 457 | func (nt NullTime) MarshalJSON() ([]byte, error) { |
300 | if nt.Valid { | 458 | if nt.Valid { |
301 | format := nt.Time.Format("2006-01-02 15:04:05") | 459 | format := nt.Time.Format("15:04:05") |
302 | return json.Marshal(format) | 460 | return json.Marshal(format) |
303 | } | 461 | } |
304 | return json.Marshal(nil) | 462 | return json.Marshal(nil) |
305 | } | 463 | } |
306 | 464 | ||
307 | // UnmarshalJSON ... | 465 | // UnmarshalJSON ... |
308 | func (nt *NullTime) UnmarshalJSON(b []byte) error { | 466 | func (nt *NullTime) UnmarshalJSON(b []byte) error { |
309 | var temp *time.Time | 467 | var temp *time.Time |
310 | var t1 time.Time | 468 | var t1 time.Time |
311 | var err error | 469 | var err error |
312 | 470 | ||
313 | s1 := string(b) | 471 | s1 := string(b) |
314 | s2 := s1[1 : len(s1)-1] | 472 | s2 := s1[1 : len(s1)-1] |
315 | if s1 == "null" { | 473 | if s1 == "null" { |
316 | temp = nil | 474 | temp = nil |
317 | } else { | 475 | } else { |
318 | t1, err = time.Parse("2006-01-02 15:04:05", s2) | 476 | t1, err = time.Parse("15:04:05", s2) |
319 | if err != nil { | 477 | if err != nil { |
320 | return err | 478 | return err |
321 | } | 479 | } |
322 | temp = &t1 | 480 | temp = &t1 |
323 | } | 481 | } |
324 | 482 | ||
325 | if temp != nil { | 483 | if temp != nil { |
326 | nt.Valid = true | 484 | nt.Valid = true |
327 | nt.Time = *temp | 485 | nt.Time = *temp |
328 | } else { | 486 | } else { |
329 | nt.Valid = false | 487 | nt.Valid = false |
330 | } | 488 | } |
331 | return nil | 489 | return nil |
332 | } | 490 | } |
333 | 491 | ||
334 | func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { | 492 | func (nt *NullTime) CastToSQL() NullTime { |
493 | return *nt | ||
494 | } | ||
495 | |||
496 | func parseSQLDateTime(str string, loc *time.Location) (t time.Time, err error) { | ||
335 | base := "0000-00-00 00:00:00.0000000" | 497 | base := "0000-00-00 00:00:00.0000000" |
336 | timeFormat := "2006-01-02 15:04:05.999999" | 498 | timeFormat := "2006-01-02 15:04:05.999999" |
337 | switch len(str) { | 499 | switch len(str) { |
338 | case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" | 500 | case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" |
339 | if str == base[:len(str)] { | 501 | if str == base[:len(str)] { |
340 | return | 502 | return |
341 | } | 503 | } |
342 | t, err = time.Parse(timeFormat[:len(str)], str) | 504 | t, err = time.Parse(timeFormat[:len(str)], str) |
343 | default: | 505 | default: |
344 | err = fmt.Errorf("invalid time string: %s", str) | 506 | err = fmt.Errorf("invalid time string: %s", str) |
345 | return | 507 | return |
346 | } | 508 | } |
347 | 509 | ||
348 | // Adjust location | 510 | // Adjust location |
349 | if err == nil && loc != time.UTC { | 511 | if err == nil && loc != time.UTC { |
350 | y, mo, d := t.Date() | 512 | y, mo, d := t.Date() |
351 | h, mi, s := t.Clock() | 513 | h, mi, s := t.Clock() |
352 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil | 514 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil |
353 | } | 515 | } |
354 | 516 | ||
355 | return | 517 | return |
356 | } | 518 | } |
519 | |||
520 | func parseSQLDate(str string, loc *time.Location) (t time.Time, err error) { | ||
521 | base := "0000-00-00" | ||
522 | timeFormat := "2006-01-02" | ||
523 | switch len(str) { | ||
524 | case 10: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" | ||
525 | if str == base[:len(str)] { | ||
526 | return | ||
527 | } | ||
528 | t, err = time.Parse(timeFormat[:len(str)], str) | ||
529 | default: | ||
530 | err = fmt.Errorf("invalid time string: %s", str) | ||
531 | return | ||
532 | } | ||
533 | |||
534 | // Adjust location | ||
535 | if err == nil && loc != time.UTC { | ||
536 | y, mo, d := t.Date() | ||
537 | h, mi, s := t.Clock() | ||
538 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil | ||
539 | } | ||
540 | |||
541 | return | ||
542 | } | ||
543 | |||
544 | func parseSQLTime(str string, loc *time.Location) (t time.Time, err error) { | ||
545 | base := "00:00:00.0000000" | ||
546 | timeFormat := "15:04:05.999999" | ||
547 | switch len(str) { | ||
548 | case 12, 15: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" | ||
549 | if str == base[:len(str)] { | ||
550 | return | ||
551 | } | ||
552 | t, err = time.Parse(timeFormat[:len(str)], str) | ||
553 | default: | ||
554 | err = fmt.Errorf("invalid time string: %s", str) | ||
555 | return | ||
556 | } | ||
557 | |||
558 | // Adjust location | ||
559 | if err == nil && loc != time.UTC { | ||
560 | y, mo, d := t.Date() | ||
561 | h, mi, s := t.Clock() | ||
562 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil | ||
563 | } | ||
564 | |||
565 | return | ||
566 | } | ||
357 | 567 |
pdfhelper/pdf.go
1 | package pdfhelper | 1 | package pdfhelper |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "strings" | 5 | "strings" |
6 | 6 | ||
7 | "git.to-net.rs/marko.tikvic/gofpdf" | 7 | "git.to-net.rs/marko.tikvic/gofpdf" |
8 | ) | 8 | ) |
9 | 9 | ||
10 | // Block ... | 10 | // Block ... |
11 | const ( | 11 | const ( |
12 | CENTER = "C" | 12 | CENTER = "C" |
13 | MIDDLE = "M" | ||
13 | LEFT = "L" | 14 | LEFT = "L" |
14 | RIGHT = "R" | 15 | RIGHT = "R" |
15 | TOP = "T" | 16 | TOP = "T" |
16 | BOTTOM = "B" | 17 | BOTTOM = "B" |
17 | FULL = "1" | 18 | FULL = "1" |
18 | NOBORDER = "" | 19 | NOBORDER = "" |
19 | ) | 20 | ) |
20 | 21 | ||
21 | const ( | 22 | const ( |
22 | CONTINUE = 0 | 23 | CONTINUE = 0 |
23 | NEWLINE = 1 | 24 | NEWLINE = 1 |
24 | BELLOW = 2 | 25 | BELLOW = 2 |
25 | ) | 26 | ) |
26 | 27 | ||
27 | const ( | 28 | const ( |
28 | cyrillicEncoding = "cp1251" | 29 | cyrillicEncoding = "cp1251" |
29 | latinEncoding = "cp1250" | 30 | latinEncoding = "cp1250" |
30 | ) | 31 | ) |
31 | 32 | ||
32 | const threeDots = "\u2056\u2056\u2056" | 33 | const threeDots = "\u2056\u2056\u2056" |
33 | 34 | ||
34 | type TableCell struct { | 35 | type TableCell struct { |
35 | W, H float64 | 36 | W, H float64 |
36 | Text string | 37 | Text string |
37 | Font, FontStyle string | 38 | Font, FontStyle string |
38 | FontSize float64 | 39 | FontSize float64 |
39 | Border string | 40 | Border string |
40 | Alignment string | 41 | Alignment string |
41 | } | 42 | } |
42 | 43 | ||
43 | // Helper ... | 44 | // Helper ... |
44 | type Helper struct { | 45 | type Helper struct { |
45 | *gofpdf.Fpdf | 46 | *gofpdf.Fpdf |
46 | translators map[string]func(string) string | 47 | translators map[string]func(string) string |
47 | } | 48 | } |
48 | 49 | ||
49 | // New ... | 50 | // New ... |
50 | func New(ori, unit, size string) *Helper { | 51 | func New(ori, unit, size string) *Helper { |
51 | helper := &Helper{ | 52 | helper := &Helper{ |
52 | Fpdf: gofpdf.New(ori, unit, size, ""), | 53 | Fpdf: gofpdf.New(ori, unit, size, ""), |
53 | } | 54 | } |
54 | 55 | ||
55 | return helper | 56 | return helper |
56 | } | 57 | } |
57 | 58 | ||
58 | func (pdf *Helper) LoadTranslators() { | 59 | func (pdf *Helper) LoadTranslators() { |
59 | pdf.translators = make(map[string]func(string) string) | 60 | pdf.translators = make(map[string]func(string) string) |
60 | pdf.translators[latinEncoding] = pdf.UnicodeTranslatorFromDescriptor(latinEncoding) | 61 | pdf.translators[latinEncoding] = pdf.UnicodeTranslatorFromDescriptor(latinEncoding) |
61 | pdf.translators[cyrillicEncoding] = pdf.UnicodeTranslatorFromDescriptor(cyrillicEncoding) | 62 | pdf.translators[cyrillicEncoding] = pdf.UnicodeTranslatorFromDescriptor(cyrillicEncoding) |
62 | } | 63 | } |
63 | 64 | ||
64 | func (pdf *Helper) DrawCell(x, y float64, c TableCell) { | 65 | func (pdf *Helper) DrawCell(x, y float64, c TableCell) { |
65 | pdf.SetXY(x, y) | 66 | pdf.SetXY(x, y) |
66 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) | 67 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) |
67 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") | 68 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") |
68 | } | 69 | } |
69 | 70 | ||
70 | func (pdf *Helper) DrawColumn(x, y float64, cells []TableCell) { | 71 | func (pdf *Helper) DrawColumn(x, y float64, cells []TableCell) { |
71 | pdf.SetXY(x, y) | 72 | pdf.SetXY(x, y) |
72 | for _, c := range cells { | 73 | for _, c := range cells { |
73 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) | 74 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) |
74 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") | 75 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, BELLOW, c.Alignment, false, 0, "") |
75 | } | 76 | } |
76 | } | 77 | } |
77 | 78 | ||
78 | func (pdf *Helper) DrawRow(x, y float64, cells []TableCell) { | 79 | func (pdf *Helper) DrawRow(x, y float64, cells []TableCell) { |
79 | pdf.SetXY(x, y) | 80 | pdf.SetXY(x, y) |
80 | for _, c := range cells { | 81 | for _, c := range cells { |
81 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) | 82 | pdf.SetFont(c.Font, c.FontStyle, c.FontSize) |
82 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, CONTINUE, c.Alignment, false, 0, "") | 83 | pdf.CellFormat(c.W, c.H, pdf.toUTF8(c.Text), c.Border, CONTINUE, c.Alignment, false, 0, "") |
83 | } | 84 | } |
84 | } | 85 | } |
85 | 86 | ||
86 | func (pdf *Helper) TextLength(txt, family, style string, size float64) float64 { | 87 | func (pdf *Helper) TextLength(txt, family, style string, size float64) float64 { |
87 | family, _, _, _ = pdf.setCorrectFontFamily(textEncoding(txt)) | 88 | family, _, _, _ = pdf.setCorrectFontFamily(textEncoding(txt)) |
88 | return pdf.Fpdf.TextLength(txt, family, style, size) | 89 | return pdf.Fpdf.TextLength(txt, family, style, size) |
89 | } | 90 | } |
90 | 91 | ||
91 | func (pdf *Helper) LimitText(text, limiter string, maxWidth float64) string { | 92 | func (pdf *Helper) LimitText(text, limiter string, maxWidth float64) string { |
92 | parts := pdf.Fpdf.SplitText(text, maxWidth) | 93 | parts := pdf.Fpdf.SplitText(text, maxWidth) |
93 | if len(parts) > 1 { | 94 | if len(parts) > 1 { |
94 | return parts[0] + limiter | 95 | return parts[0] + limiter |
95 | } | 96 | } |
96 | 97 | ||
97 | return text | 98 | return text |
98 | } | 99 | } |
99 | 100 | ||
100 | // InsertImage ... | 101 | // InsertImage ... |
101 | func (pdf *Helper) InsertImage(img string, x, y, w, h float64) { | 102 | func (pdf *Helper) InsertImage(img string, x, y, w, h float64) { |
102 | imgType := "" | 103 | imgType := "" |
103 | if parts := strings.Split(img, "."); len(parts) >= 2 { | 104 | if parts := strings.Split(img, "."); len(parts) >= 2 { |
104 | imgType = parts[len(parts)-1] | 105 | imgType = parts[len(parts)-1] |
105 | } | 106 | } |
106 | opt := gofpdf.ImageOptions{ | 107 | opt := gofpdf.ImageOptions{ |
107 | ImageType: imgType, | 108 | ImageType: imgType, |
108 | ReadDpi: false, | 109 | ReadDpi: false, |
109 | AllowNegativePosition: false, | 110 | AllowNegativePosition: false, |
110 | } | 111 | } |
111 | autoBreak := false // if it's not false then you can't draw the image at an arbitrary height (y position) | 112 | autoBreak := false // if it's not false then you can't draw the image at an arbitrary height (y position) |
112 | pdf.ImageOptions(img, x, y, w, h, autoBreak, opt, 0, "") | 113 | pdf.ImageOptions(img, x, y, w, h, autoBreak, opt, 0, "") |
113 | } | 114 | } |
114 | 115 | ||
115 | func (pdf *Helper) PageHasSpace(requiredHeight float64) bool { | 116 | func (pdf *Helper) PageHasSpace(requiredHeight float64) bool { |
116 | _, h := pdf.GetPageSize() | 117 | _, h := pdf.GetPageSize() |
117 | _, _, _, bot := pdf.GetMargins() | 118 | _, _, _, bot := pdf.GetMargins() |
118 | return (h - bot - pdf.GetY()) > requiredHeight | 119 | return (h - bot - pdf.GetY()) > requiredHeight |
119 | } | 120 | } |
120 | 121 | ||
121 | // DrawBox ... | 122 | // DrawBox ... |
122 | func (pdf Helper) DrawBox(x0, y0, w, h float64) { | 123 | func (pdf Helper) DrawBox(x0, y0, w, h float64) { |
123 | pdf.Line(x0, y0, x0+w, y0) | 124 | pdf.Line(x0, y0, x0+w, y0) |
124 | pdf.Line(x0+w, y0, x0+w, y0+h) | 125 | pdf.Line(x0+w, y0, x0+w, y0+h) |
125 | pdf.Line(x0+w, y0+h, x0, y0+h) | 126 | pdf.Line(x0+w, y0+h, x0, y0+h) |
126 | pdf.Line(x0, y0+h, x0, y0) | 127 | pdf.Line(x0, y0+h, x0, y0) |
127 | } | 128 | } |
128 | 129 | ||
129 | // Strana %d/{TotalPages} | 130 | // Strana %d/{TotalPages} |
130 | func (pdf *Helper) InsertPageNumber(x, y float64, format string) { | 131 | func (pdf *Helper) InsertPageNumber(x, y float64, format string) { |
131 | num := fmt.Sprintf(format, pdf.PageNo()) | 132 | num := fmt.Sprintf(format, pdf.PageNo()) |
132 | pdf.DrawColumn(x, y, []TableCell{{10, 1, num, "DejaVuSans", "", 8, NOBORDER, LEFT}}) | 133 | pdf.DrawColumn(x, y, []TableCell{{10, 1, num, "DejaVuSans", "", 8, NOBORDER, LEFT}}) |
133 | } | 134 | } |
134 | 135 | ||
135 | func (pdf *Helper) SuperscriptText(x, y, cw, ch float64, text, script string) { | 136 | func (pdf *Helper) SuperscriptText(x, y, cw, ch float64, text, script string) { |
136 | family, style, size, sizeU := pdf.setCorrectFontFamily(textEncoding(text)) | 137 | family, style, size, sizeU := pdf.setCorrectFontFamily(textEncoding(text)) |
137 | 138 | ||
138 | pdf.DrawCell(x, y, TableCell{cw, ch, text, family, style, size, NOBORDER, LEFT}) | 139 | pdf.DrawCell(x, y, TableCell{cw, ch, text, family, style, size, NOBORDER, LEFT}) |
139 | 140 | ||
140 | sx := x + pdf.TextLength(text, family, style, size) | 141 | sx := x + pdf.TextLength(text, family, style, size) |
141 | sy := y - sizeU*0.2 | 142 | sy := y - sizeU*0.2 |
142 | pdf.DrawCell(sx, sy, TableCell{cw, ch, script, family, style, size - 2, NOBORDER, LEFT}) | 143 | pdf.DrawCell(sx, sy, TableCell{cw, ch, script, family, style, size - 2, NOBORDER, LEFT}) |
143 | } | 144 | } |
144 | 145 | ||
145 | // toUTF8 ... | 146 | // toUTF8 ... |
146 | func (pdf *Helper) toUTF8(s string) string { | 147 | func (pdf *Helper) toUTF8(s string) string { |
147 | encoding := textEncoding(s) | 148 | encoding := textEncoding(s) |
148 | pdf.setCorrectFontFamily(encoding) | 149 | pdf.setCorrectFontFamily(encoding) |
149 | translator, ok := pdf.translators[encoding] | 150 | translator, ok := pdf.translators[encoding] |
150 | if !ok { | 151 | if !ok { |
151 | return "" | 152 | return "" |
152 | } | 153 | } |
153 | return translator(s) | 154 | return translator(s) |
154 | } | 155 | } |
155 | 156 | ||
156 | func textEncoding(s string) string { | 157 | func textEncoding(s string) string { |
157 | encoding := latinEncoding | 158 | encoding := latinEncoding |
158 | runes := []rune(s) | 159 | runes := []rune(s) |
159 | for _, r := range runes { | 160 | for _, r := range runes { |
160 | if uint64(r) >= 0x0402 && uint64(r) <= 0x044f { | 161 | if uint64(r) >= 0x0402 && uint64(r) <= 0x044f { |
161 | encoding = cyrillicEncoding | 162 | encoding = cyrillicEncoding |
162 | break | 163 | break |
163 | } | 164 | } |
164 | } | 165 | } |
165 | return encoding | 166 | return encoding |
166 | } | 167 | } |
167 | 168 | ||
168 | func (pdf *Helper) setCorrectFontFamily(enc string) (family, style string, ptSize, unitSize float64) { | 169 | func (pdf *Helper) setCorrectFontFamily(enc string) (family, style string, ptSize, unitSize float64) { |
169 | family, style, ptSize, unitSize = pdf.GetFontInfo() | 170 | family, style, ptSize, unitSize = pdf.GetFontInfo() |
170 | if enc == cyrillicEncoding { | 171 | if enc == cyrillicEncoding { |
171 | if !strings.HasSuffix(family, "cyrillic") { | 172 | if !strings.HasSuffix(family, "cyrillic") { |
172 | family += "cyrillic" | 173 | family += "cyrillic" |
173 | } | 174 | } |
174 | } else { | 175 | } else { |
175 | if strings.HasSuffix(family, "cyrillic") { | 176 | if strings.HasSuffix(family, "cyrillic") { |
176 | family = strings.TrimSuffix(family, "cyrillic") | 177 | family = strings.TrimSuffix(family, "cyrillic") |
177 | } | 178 | } |
178 | } | 179 | } |
179 | pdf.SetFont(family, style, ptSize) | 180 | pdf.SetFont(family, style, ptSize) |
180 | return family, style, ptSize, unitSize | 181 | return family, style, ptSize, unitSize |
181 | } | 182 | } |
182 | 183 |