Commit 07daaf4e563562e18240031d27d8936d45d0af2d

Authored by Marko Tikvić
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
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
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