Commit 0c71c5487302d1b7d5dda0210fe12d2be4a034f8
1 parent
6cc94a06e9
Exists in
master
minor changes
Showing
5 changed files
with
73 additions
and
17 deletions
Show diff stats
date_util.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "strings" | 5 | "strings" |
6 | "time" | 6 | "time" |
7 | ) | 7 | ) |
8 | 8 | ||
9 | const ( | 9 | const ( |
10 | YYYYMMDD_sl = "2006/01/02" | 10 | YYYYMMDD_sl = "2006/01/02" |
11 | YYYYMMDD_ds = "2006-01-02" | 11 | YYYYMMDD_ds = "2006-01-02" |
12 | YYYYMMDD_dt = "2006.01.02." | 12 | YYYYMMDD_dt = "2006.01.02." |
13 | 13 | ||
14 | DDMMYYYY_sl = "02/01/2006" | 14 | DDMMYYYY_sl = "02/01/2006" |
15 | DDMMYYYY_ds = "02-01-2006" | 15 | DDMMYYYY_ds = "02-01-2006" |
16 | DDMMYYYY_dt = "02.01.2006." | 16 | DDMMYYYY_dt = "02.01.2006." |
17 | 17 | ||
18 | YYYYMMDD_HHMMSS_sl = "2006/01/02 15:04:05" | 18 | YYYYMMDD_HHMMSS_sl = "2006/01/02 15:04:05" |
19 | YYYYMMDD_HHMMSS_ds = "2006-01-02 15:04:05" | 19 | YYYYMMDD_HHMMSS_ds = "2006-01-02 15:04:05" |
20 | YYYYMMDD_HHMMSS_dt = "2006.01.02. 15:04:05" | 20 | YYYYMMDD_HHMMSS_dt = "2006.01.02. 15:04:05" |
21 | 21 | ||
22 | DDMMYYYY_HHMMSS_sl = "02/01/2006 15:04:05" | 22 | DDMMYYYY_HHMMSS_sl = "02/01/2006 15:04:05" |
23 | DDMMYYYY_HHMMSS_ds = "02-01-2006 15:04:05" | 23 | DDMMYYYY_HHMMSS_ds = "02-01-2006 15:04:05" |
24 | DDMMYYYY_HHMMSS_dt = "02.01.2006. 15:04:05" | 24 | DDMMYYYY_HHMMSS_dt = "02.01.2006. 15:04:05" |
25 | ) | 25 | ) |
26 | 26 | ||
27 | const DaySeconds = 24 * 60 * 60 | ||
28 | |||
27 | var ( | 29 | var ( |
28 | regularYear = [12]int64{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} | 30 | regularYear = [12]int64{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} |
29 | leapYear = [12]int64{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} | 31 | leapYear = [12]int64{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} |
30 | ) | 32 | ) |
31 | 33 | ||
32 | func Systime() int64 { | 34 | func Systime() int64 { |
33 | return time.Now().Unix() | 35 | return time.Now().Unix() |
34 | } | 36 | } |
35 | 37 | ||
36 | func DateToEpoch(date, format string) int64 { | 38 | func DateToEpoch(date, format string) int64 { |
37 | t, err := time.Parse(format, date) | 39 | t, err := time.Parse(format, date) |
38 | if err != nil { | 40 | if err != nil { |
39 | fmt.Println(err.Error()) | 41 | fmt.Println(err.Error()) |
40 | return 0 | 42 | return 0 |
41 | } | 43 | } |
42 | return t.Unix() | 44 | return t.Unix() |
43 | } | 45 | } |
44 | 46 | ||
45 | func EpochToDate(e int64, format string) string { | 47 | func EpochToDate(e int64, format string) string { |
46 | return time.Unix(e, 0).Format(format) | 48 | return time.Unix(e, 0).Format(format) |
47 | } | 49 | } |
48 | 50 | ||
49 | func EpochToDayMonthYear(timestamp int64) (d, m, y int64) { | 51 | func EpochToDayMonthYear(timestamp int64) (d, m, y int64) { |
50 | datestring := EpochToDate(timestamp, DDMMYYYY_sl) | 52 | datestring := EpochToDate(timestamp, DDMMYYYY_sl) |
51 | parts := strings.Split(datestring, "/") | 53 | parts := strings.Split(datestring, "/") |
52 | d = StringToInt64(parts[0]) | 54 | d = StringToInt64(parts[0]) |
53 | m = StringToInt64(parts[1]) | 55 | m = StringToInt64(parts[1]) |
54 | y = StringToInt64(parts[2]) | 56 | y = StringToInt64(parts[2]) |
55 | return d, m, y | 57 | return d, m, y |
56 | } | 58 | } |
57 | 59 | ||
58 | func DaysInMonth(year, month int64) int64 { | 60 | func DaysInMonth(year, month int64) int64 { |
59 | if month < 1 || month > 12 { | 61 | if month < 1 || month > 12 { |
60 | return 0 | 62 | return 0 |
61 | } | 63 | } |
62 | if IsLeapYear(year) { | 64 | if IsLeapYear(year) { |
63 | return leapYear[month-1] | 65 | return leapYear[month-1] |
64 | } | 66 | } |
65 | return regularYear[month-1] | 67 | return regularYear[month-1] |
66 | } | 68 | } |
67 | 69 | ||
68 | func IsLeapYear(year int64) bool { | 70 | func IsLeapYear(year int64) bool { |
69 | return year%4 == 0 && !((year%100 == 0) && (year%400 != 0)) | 71 | return year%4 == 0 && !((year%100 == 0) && (year%400 != 0)) |
70 | } | 72 | } |
71 | 73 | ||
72 | // FirstDayOfNextMonthEpoch ... | 74 | // FirstDayOfNextMonthEpoch ... |
73 | func FirstDayOfNextMonthEpoch(e int64) int64 { | 75 | func NextMonths1st(e int64) int64 { |
74 | d, m, y := EpochToDayMonthYear(e) | 76 | d, m, y := EpochToDayMonthYear(e) |
75 | m++ | 77 | m++ |
76 | if m > 12 { | 78 | if m > 12 { |
77 | m = 1 | 79 | m = 1 |
78 | y++ | 80 | y++ |
79 | } | 81 | } |
80 | d = 1 | 82 | d = 1 |
81 | 83 | ||
82 | date := fmt.Sprintf("%02d/%02d/%d", d, m, y) | 84 | date := fmt.Sprintf("%02d/%02d/%d", d, m, y) |
83 | 85 | ||
84 | return DateToEpoch(date, DDMMYYYY_sl) | 86 | return DateToEpoch(date, DDMMYYYY_sl) |
85 | } | 87 | } |
88 | |||
89 | func SameDate(e1, e2 int64) bool { | ||
90 | d1, m1, y1 := EpochToDayMonthYear(e1) | ||
91 | d2, m2, y2 := EpochToDayMonthYear(e2) | ||
92 | |||
93 | if d1 == d2 && m1 == m2 && y1 == y2 { | ||
94 | return true | ||
95 | } | ||
96 | |||
97 | return false | ||
98 | } | ||
99 | |||
100 | func ToStartOfDay(d int64) int64 { | ||
101 | rem := d % DaySeconds | ||
102 | if rem != 0 { | ||
103 | d -= rem | ||
104 | } | ||
105 | return d | ||
106 | } | ||
107 | |||
108 | func ParseTime(date, format string) time.Time { | ||
109 | t, err := time.Parse(format, date) | ||
110 | if err != nil { | ||
111 | fmt.Println(err.Error()) | ||
112 | } | ||
113 | return t | ||
114 | } | ||
86 | 115 |
float_util.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "math" | 5 | "math" |
6 | "math/big" | 6 | "math/big" |
7 | "strings" | 7 | "strings" |
8 | ) | 8 | ) |
9 | 9 | ||
10 | func RoundFloat64(f float64, dec int) float64 { | 10 | func RoundFloat64(f float64, dec int) float64 { |
11 | p := math.Pow(10, float64(dec)) | 11 | p := math.Pow(10, float64(dec)) |
12 | return math.Round(f*p) / p | 12 | return math.Round(f*p) / p |
13 | } | 13 | } |
14 | 14 | ||
15 | func NewBF(f float64, prec uint) *big.Float { | 15 | func NewBF(f float64, prec uint) *big.Float { |
16 | x := big.NewFloat(f) | 16 | x := big.NewFloat(f) |
17 | x.SetPrec(prec) | 17 | x.SetPrec(prec) |
18 | return x | 18 | return x |
19 | } | 19 | } |
20 | 20 | ||
21 | func AddBF(x, y *big.Float) *big.Float { | 21 | func AddBF(x, y *big.Float) *big.Float { |
22 | z := big.NewFloat(0.0) | 22 | z := big.NewFloat(0.0) |
23 | z.SetPrec(x.Prec()) | 23 | z.SetPrec(x.Prec()) |
24 | z.Add(x, y) | 24 | z.Add(x, y) |
25 | return z | 25 | return z |
26 | } | 26 | } |
27 | 27 | ||
28 | func SubBF(x, y *big.Float) *big.Float { | 28 | func SubBF(x, y *big.Float) *big.Float { |
29 | z := big.NewFloat(0.0) | 29 | z := big.NewFloat(0.0) |
30 | z.SetPrec(x.Prec()) | 30 | z.SetPrec(x.Prec()) |
31 | 31 | ||
32 | yneg := big.NewFloat(0.0) | 32 | yneg := big.NewFloat(0.0) |
33 | yneg.Neg(y) | 33 | yneg.Neg(y) |
34 | 34 | ||
35 | z.Add(x, yneg) | 35 | z.Add(x, yneg) |
36 | return z | 36 | return z |
37 | } | 37 | } |
38 | 38 | ||
39 | func MulBF(x, y *big.Float) *big.Float { | 39 | func MulBF(x, y *big.Float) *big.Float { |
40 | z := big.NewFloat(0.0) | 40 | z := big.NewFloat(0.0) |
41 | z.SetPrec(x.Prec()) | 41 | z.SetPrec(x.Prec()) |
42 | z.Mul(x, y) | 42 | z.Mul(x, y) |
43 | return z | 43 | return z |
44 | } | 44 | } |
45 | 45 | ||
46 | func DivBF(x, y *big.Float) *big.Float { | 46 | func DivBF(x, y *big.Float) *big.Float { |
47 | z := big.NewFloat(0.0) | 47 | z := big.NewFloat(0.0) |
48 | z.SetPrec(x.Prec()) | 48 | z.SetPrec(x.Prec()) |
49 | z.Quo(x, y) | 49 | z.Quo(x, y) |
50 | return z | 50 | return z |
51 | } | 51 | } |
52 | 52 | ||
53 | func BFtoFloat(f *big.Float) float64 { | 53 | func BFtoFloat(f *big.Float) float64 { |
54 | v, _ := f.Float64() | 54 | v, _ := f.Float64() |
55 | return v | 55 | return v |
56 | } | 56 | } |
57 | 57 | ||
58 | func Float64ToString(f float64) string { | 58 | func Float64ToString(f float64) string { |
59 | return fmt.Sprintf("%.2f", f) | 59 | return fmt.Sprintf("%.2f", f) |
60 | } | 60 | } |
61 | 61 | ||
62 | func Float64PtrToString(f *float64) string { | 62 | func Float64PtrToString(f *float64) string { |
63 | if f == nil { | 63 | if f == nil { |
64 | return "" | 64 | return "" |
65 | } | 65 | } |
66 | return fmt.Sprintf("%.2f", *f) | 66 | return fmt.Sprintf("%.2f", *f) |
67 | } | 67 | } |
68 | 68 | ||
69 | func FormatFloat64Number(f float64, dec int) string { | 69 | func FormatFloat64Number(f float64, dec int) string { |
70 | res := "" | 70 | res := "" |
71 | 71 | ||
72 | f = RoundFloat64(f, dec) | 72 | f = RoundFloat64(f, dec) |
73 | 73 | ||
74 | i := int64(f) | 74 | i := int64(f) |
75 | 75 | ||
76 | format := fmt.Sprintf("%%.%df", dec) | 76 | format := fmt.Sprintf("%%.%df", dec) |
77 | parts := strings.Split(fmt.Sprintf(format, f-float64(i)), ".") | 77 | parts := strings.Split(fmt.Sprintf(format, f-float64(i)), ".") |
78 | 78 | ||
79 | decimals := parts[1] | 79 | decimals := parts[1] |
80 | 80 | ||
81 | res = FormatInt64Number(i) + "," + decimals | 81 | res = FormatInt64Number(i) + "," + decimals |
82 | 82 | ||
83 | return res | 83 | return res |
84 | } | 84 | } |
85 | 85 | ||
86 | func MaxFlaot64(vars ...float64) (max float64) { | 86 | func MaxFloat64(vars ...float64) (max float64) { |
87 | max = vars[0] | 87 | max = vars[0] |
88 | for _, v := range vars { | 88 | for _, v := range vars { |
89 | if v > max { | 89 | if v > max { |
90 | max = v | 90 | max = v |
91 | } | 91 | } |
92 | } | 92 | } |
93 | return max | 93 | return max |
94 | } | 94 | } |
95 | 95 |
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 | // CastToSQL ... | 57 | // CastToSQL ... |
58 | func (nb *NullBool) CastToSQL() 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 | // Val ... | ||
85 | func (ns *NullString) Val() string { | ||
86 | return ns.String | ||
87 | } | ||
88 | |||
84 | // MarshalJSON ... | 89 | // MarshalJSON ... |
85 | func (ns NullString) MarshalJSON() ([]byte, error) { | 90 | func (ns NullString) MarshalJSON() ([]byte, error) { |
86 | if ns.Valid { | 91 | if ns.Valid { |
87 | return json.Marshal(ns.String) | 92 | return json.Marshal(ns.String) |
88 | } | 93 | } |
89 | return json.Marshal(nil) | 94 | return json.Marshal(nil) |
90 | } | 95 | } |
91 | 96 | ||
92 | // UnmarshalJSON ... | 97 | // UnmarshalJSON ... |
93 | func (ns *NullString) UnmarshalJSON(b []byte) error { | 98 | func (ns *NullString) UnmarshalJSON(b []byte) error { |
94 | var temp *string | 99 | var temp *string |
95 | if err := json.Unmarshal(b, &temp); err != nil { | 100 | if err := json.Unmarshal(b, &temp); err != nil { |
96 | return err | 101 | return err |
97 | } | 102 | } |
98 | if temp != nil { | 103 | if temp != nil { |
99 | ns.Valid = true | 104 | ns.Valid = true |
100 | ns.String = *temp | 105 | ns.String = *temp |
101 | } else { | 106 | } else { |
102 | ns.Valid = false | 107 | ns.Valid = false |
103 | } | 108 | } |
104 | return nil | 109 | return nil |
105 | } | 110 | } |
106 | 111 | ||
107 | // CastToSQL ... | 112 | // CastToSQL ... |
108 | func (ns *NullString) CastToSQL() sql.NullString { | 113 | func (ns *NullString) CastToSQL() sql.NullString { |
109 | return sql.NullString(*ns) | 114 | return sql.NullString(*ns) |
110 | } | 115 | } |
111 | 116 | ||
112 | // NullInt64 is a wrapper for sql.NullInt64 with added JSON (un)marshalling | 117 | // NullInt64 is a wrapper for sql.NullInt64 with added JSON (un)marshalling |
113 | type NullInt64 sql.NullInt64 | 118 | type NullInt64 sql.NullInt64 |
114 | 119 | ||
115 | // Scan ... | 120 | // Scan ... |
116 | func (ni *NullInt64) Scan(value interface{}) error { | 121 | func (ni *NullInt64) Scan(value interface{}) error { |
117 | var i sql.NullInt64 | 122 | var i sql.NullInt64 |
118 | if err := i.Scan(value); err != nil { | 123 | if err := i.Scan(value); err != nil { |
119 | ni.Int64, ni.Valid = 0, false | 124 | ni.Int64, ni.Valid = 0, false |
120 | return err | 125 | return err |
121 | } | 126 | } |
122 | ni.Int64, ni.Valid = i.Int64, i.Valid | 127 | ni.Int64, ni.Valid = i.Int64, i.Valid |
123 | return nil | 128 | return nil |
124 | } | 129 | } |
125 | 130 | ||
126 | // ScanPtr ... | 131 | // ScanPtr ... |
127 | func (ni *NullInt64) ScanPtr(v interface{}) error { | 132 | func (ni *NullInt64) ScanPtr(v interface{}) error { |
128 | if ip, ok := v.(*int64); ok && ip != nil { | 133 | if ip, ok := v.(*int64); ok && ip != nil { |
129 | return ni.Scan(*ip) | 134 | return ni.Scan(*ip) |
130 | } | 135 | } |
131 | return nil | 136 | return nil |
132 | } | 137 | } |
133 | 138 | ||
134 | // Value ... | 139 | // Value ... |
135 | func (ni *NullInt64) Value() (driver.Value, error) { | 140 | func (ni *NullInt64) Value() (driver.Value, error) { |
136 | if !ni.Valid { | 141 | if !ni.Valid { |
137 | return nil, nil | 142 | return nil, nil |
138 | } | 143 | } |
139 | return ni.Int64, nil | 144 | return ni.Int64, nil |
140 | } | 145 | } |
141 | 146 | ||
142 | func (ni *NullInt64) Val() int64 { | 147 | func (ni *NullInt64) Val() int64 { |
143 | return ni.Int64 | 148 | return ni.Int64 |
144 | } | 149 | } |
145 | 150 | ||
146 | // Add | 151 | // Add |
147 | func (ni *NullInt64) Add(i NullInt64) { | 152 | func (ni *NullInt64) Add(i NullInt64) { |
148 | ni.Valid = true | 153 | ni.Valid = true |
149 | ni.Int64 += i.Int64 | 154 | ni.Int64 += i.Int64 |
150 | } | 155 | } |
151 | 156 | ||
152 | func (ni *NullInt64) Set(i int64) { | 157 | func (ni *NullInt64) Set(i int64) { |
153 | ni.Valid = true | 158 | ni.Valid = true |
154 | ni.Int64 = i | 159 | ni.Int64 = i |
155 | } | 160 | } |
156 | 161 | ||
157 | // MarshalJSON ... | 162 | // MarshalJSON ... |
158 | func (ni NullInt64) MarshalJSON() ([]byte, error) { | 163 | func (ni NullInt64) MarshalJSON() ([]byte, error) { |
159 | if ni.Valid { | 164 | if ni.Valid { |
160 | return json.Marshal(ni.Int64) | 165 | return json.Marshal(ni.Int64) |
161 | } | 166 | } |
162 | return json.Marshal(nil) | 167 | return json.Marshal(nil) |
163 | } | 168 | } |
164 | 169 | ||
165 | // UnmarshalJSON ... | 170 | // UnmarshalJSON ... |
166 | func (ni *NullInt64) UnmarshalJSON(b []byte) error { | 171 | func (ni *NullInt64) UnmarshalJSON(b []byte) error { |
167 | var temp *int64 | 172 | var temp *int64 |
168 | if err := json.Unmarshal(b, &temp); err != nil { | 173 | if err := json.Unmarshal(b, &temp); err != nil { |
169 | return err | 174 | return err |
170 | } | 175 | } |
171 | if temp != nil { | 176 | if temp != nil { |
172 | ni.Valid = true | 177 | ni.Valid = true |
173 | ni.Int64 = *temp | 178 | ni.Int64 = *temp |
174 | } else { | 179 | } else { |
175 | ni.Valid = false | 180 | ni.Valid = false |
176 | } | 181 | } |
177 | return nil | 182 | return nil |
178 | } | 183 | } |
179 | 184 | ||
180 | // CastToSQL ... | 185 | // CastToSQL ... |
181 | func (ni *NullInt64) CastToSQL() sql.NullInt64 { | 186 | func (ni *NullInt64) CastToSQL() sql.NullInt64 { |
182 | return sql.NullInt64(*ni) | 187 | return sql.NullInt64(*ni) |
183 | } | 188 | } |
184 | 189 | ||
185 | // NullFloat64 is a wrapper for sql.NullFloat64 with added JSON (un)marshalling | 190 | // NullFloat64 is a wrapper for sql.NullFloat64 with added JSON (un)marshalling |
186 | type NullFloat64 sql.NullFloat64 | 191 | type NullFloat64 sql.NullFloat64 |
187 | 192 | ||
188 | // Scan ... | 193 | // Scan ... |
189 | func (nf *NullFloat64) Scan(value interface{}) error { | 194 | func (nf *NullFloat64) Scan(value interface{}) error { |
190 | var f sql.NullFloat64 | 195 | var f sql.NullFloat64 |
191 | if err := f.Scan(value); err != nil { | 196 | if err := f.Scan(value); err != nil { |
192 | nf.Float64, nf.Valid = 0.0, false | 197 | nf.Float64, nf.Valid = 0.0, false |
193 | return err | 198 | return err |
194 | } | 199 | } |
195 | nf.Float64, nf.Valid = f.Float64, f.Valid | 200 | nf.Float64, nf.Valid = f.Float64, f.Valid |
196 | return nil | 201 | return nil |
197 | } | 202 | } |
198 | 203 | ||
199 | // ScanPtr ... | 204 | // ScanPtr ... |
200 | func (nf *NullFloat64) ScanPtr(v interface{}) error { | 205 | func (nf *NullFloat64) ScanPtr(v interface{}) error { |
201 | if fp, ok := v.(*float64); ok && fp != nil { | 206 | if fp, ok := v.(*float64); ok && fp != nil { |
202 | return nf.Scan(*fp) | 207 | return nf.Scan(*fp) |
203 | } | 208 | } |
204 | return nil | 209 | return nil |
205 | } | 210 | } |
206 | 211 | ||
207 | // Value ... | 212 | // Value ... |
208 | func (nf *NullFloat64) Value() (driver.Value, error) { | 213 | func (nf *NullFloat64) Value() (driver.Value, error) { |
209 | if !nf.Valid { | 214 | if !nf.Valid { |
210 | return nil, nil | 215 | return nil, nil |
211 | } | 216 | } |
212 | return nf.Float64, nil | 217 | return nf.Float64, nil |
213 | } | 218 | } |
214 | 219 | ||
215 | // Val ... | 220 | // Val ... |
216 | func (nf *NullFloat64) Val() float64 { | 221 | func (nf *NullFloat64) Val() float64 { |
217 | return nf.Float64 | 222 | return nf.Float64 |
218 | } | 223 | } |
219 | 224 | ||
220 | // Add ... | 225 | // Add ... |
221 | func (nf *NullFloat64) Add(f NullFloat64) { | 226 | func (nf *NullFloat64) Add(f NullFloat64) { |
222 | nf.Valid = true | 227 | nf.Valid = true |
223 | nf.Float64 += f.Float64 | 228 | nf.Float64 += f.Float64 |
224 | } | 229 | } |
225 | 230 | ||
226 | func (nf *NullFloat64) Set(f float64) { | 231 | func (nf *NullFloat64) Set(f float64) { |
227 | nf.Valid = true | 232 | nf.Valid = true |
228 | nf.Float64 = f | 233 | nf.Float64 = f |
229 | } | 234 | } |
230 | 235 | ||
231 | // MarshalJSON ... | 236 | // MarshalJSON ... |
232 | func (nf NullFloat64) MarshalJSON() ([]byte, error) { | 237 | func (nf NullFloat64) MarshalJSON() ([]byte, error) { |
233 | if nf.Valid { | 238 | if nf.Valid { |
234 | return json.Marshal(nf.Float64) | 239 | return json.Marshal(nf.Float64) |
235 | } | 240 | } |
236 | return json.Marshal(nil) | 241 | return json.Marshal(nil) |
237 | } | 242 | } |
238 | 243 | ||
239 | // UnmarshalJSON ... | 244 | // UnmarshalJSON ... |
240 | func (nf *NullFloat64) UnmarshalJSON(b []byte) error { | 245 | func (nf *NullFloat64) UnmarshalJSON(b []byte) error { |
241 | var temp *float64 | 246 | var temp *float64 |
242 | if err := json.Unmarshal(b, &temp); err != nil { | 247 | if err := json.Unmarshal(b, &temp); err != nil { |
243 | return err | 248 | return err |
244 | } | 249 | } |
245 | if temp != nil { | 250 | if temp != nil { |
246 | nf.Valid = true | 251 | nf.Valid = true |
247 | nf.Float64 = *temp | 252 | nf.Float64 = *temp |
248 | } else { | 253 | } else { |
249 | nf.Valid = false | 254 | nf.Valid = false |
250 | } | 255 | } |
251 | return nil | 256 | return nil |
252 | } | 257 | } |
253 | 258 | ||
254 | // CastToSQL ... | 259 | // CastToSQL ... |
255 | func (nf *NullFloat64) CastToSQL() sql.NullFloat64 { | 260 | func (nf *NullFloat64) CastToSQL() sql.NullFloat64 { |
256 | return sql.NullFloat64(*nf) | 261 | return sql.NullFloat64(*nf) |
257 | } | 262 | } |
258 | 263 | ||
259 | // NullDateTime ... | 264 | // NullDateTime ... |
260 | type NullDateTime struct { | 265 | type NullDateTime struct { |
261 | Time time.Time | 266 | Time time.Time |
262 | Valid bool // Valid is true if Time is not NULL | 267 | Valid bool // Valid is true if Time is not NULL |
263 | } | 268 | } |
264 | 269 | ||
265 | // Scan ... | 270 | // Scan ... |
266 | func (nt *NullDateTime) Scan(value interface{}) (err error) { | 271 | func (nt *NullDateTime) Scan(value interface{}) (err error) { |
267 | if value == nil { | 272 | if value == nil { |
268 | nt.Time, nt.Valid = time.Time{}, false | 273 | nt.Time, nt.Valid = time.Time{}, false |
269 | return | 274 | return |
270 | } | 275 | } |
271 | 276 | ||
272 | switch v := value.(type) { | 277 | switch v := value.(type) { |
273 | case time.Time: | 278 | case time.Time: |
274 | nt.Time, nt.Valid = v, true | 279 | nt.Time, nt.Valid = v, true |
275 | return | 280 | return |
276 | case []byte: | 281 | case []byte: |
277 | nt.Time, err = parseSQLDateTime(string(v), time.UTC) | 282 | nt.Time, err = parseSQLDateTime(string(v), time.UTC) |
278 | nt.Valid = (err == nil) | 283 | nt.Valid = (err == nil) |
279 | return | 284 | return |
280 | case string: | 285 | case string: |
281 | nt.Time, err = parseSQLDateTime(v, time.UTC) | 286 | nt.Time, err = parseSQLDateTime(v, time.UTC) |
282 | nt.Valid = (err == nil) | 287 | nt.Valid = (err == nil) |
283 | return | 288 | return |
284 | } | 289 | } |
285 | 290 | ||
286 | nt.Valid = false | 291 | nt.Valid = false |
287 | return fmt.Errorf("Can't convert %T to time.Time", value) | 292 | return fmt.Errorf("Can't convert %T to time.Time", value) |
288 | } | 293 | } |
289 | 294 | ||
290 | // Value implements the driver Valuer interface. | 295 | // Value implements the driver Valuer interface. |
291 | func (nt NullDateTime) Value() (driver.Value, error) { | 296 | func (nt NullDateTime) Value() (driver.Value, error) { |
292 | if !nt.Valid { | 297 | if !nt.Valid { |
293 | return nil, nil | 298 | return nil, nil |
294 | } | 299 | } |
295 | return nt.Time, nil | 300 | return nt.Time, nil |
296 | } | 301 | } |
297 | 302 | ||
298 | // MarshalJSON ... | 303 | // MarshalJSON ... |
299 | func (nt NullDateTime) MarshalJSON() ([]byte, error) { | 304 | func (nt NullDateTime) MarshalJSON() ([]byte, error) { |
300 | if nt.Valid { | 305 | if nt.Valid { |
301 | format := nt.Time.Format("2006-01-02 15:04:05") | 306 | format := nt.Time.Format("2006-01-02 15:04:05") |
302 | return json.Marshal(format) | 307 | return json.Marshal(format) |
303 | } | 308 | } |
304 | return json.Marshal(nil) | 309 | return json.Marshal(nil) |
305 | } | 310 | } |
306 | 311 | ||
307 | // UnmarshalJSON ... | 312 | // UnmarshalJSON ... |
308 | func (nt *NullDateTime) UnmarshalJSON(b []byte) error { | 313 | func (nt *NullDateTime) UnmarshalJSON(b []byte) error { |
309 | var temp *time.Time | 314 | var temp *time.Time |
310 | var t1 time.Time | 315 | var t1 time.Time |
311 | var err error | 316 | var err error |
312 | 317 | ||
313 | s1 := string(b) | 318 | s1 := string(b) |
314 | s2 := s1[1 : len(s1)-1] | 319 | s2 := s1[1 : len(s1)-1] |
315 | if s1 == "null" { | 320 | if s1 == "null" { |
316 | temp = nil | 321 | temp = nil |
317 | } else { | 322 | } else { |
318 | t1, err = time.Parse("2006-01-02 15:04:05", s2) | 323 | t1, err = time.Parse("2006-01-02 15:04:05", s2) |
319 | if err != nil { | 324 | if err != nil { |
320 | return err | 325 | return err |
321 | } | 326 | } |
322 | temp = &t1 | 327 | temp = &t1 |
323 | } | 328 | } |
324 | 329 | ||
325 | if temp != nil { | 330 | if temp != nil { |
326 | nt.Valid = true | 331 | nt.Valid = true |
327 | nt.Time = *temp | 332 | nt.Time = *temp |
328 | } else { | 333 | } else { |
329 | nt.Valid = false | 334 | nt.Valid = false |
330 | } | 335 | } |
331 | return nil | 336 | return nil |
332 | } | 337 | } |
333 | 338 | ||
334 | func (nt *NullDateTime) CastToSQL() NullDateTime { | 339 | func (nt *NullDateTime) CastToSQL() NullDateTime { |
335 | return *nt | 340 | return *nt |
336 | } | 341 | } |
337 | 342 | ||
338 | func parseSQLDateTime(str string, loc *time.Location) (t time.Time, err error) { | 343 | func parseSQLDateTime(str string, loc *time.Location) (t time.Time, err error) { |
339 | base := "0000-00-00 00:00:00.0000000" | 344 | base := "0000-00-00 00:00:00.0000000" |
340 | timeFormat := "2006-01-02 15:04:05.999999" | 345 | timeFormat := "2006-01-02 15:04:05.999999" |
341 | switch len(str) { | 346 | switch len(str) { |
342 | case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" | 347 | case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM" |
343 | if str == base[:len(str)] { | 348 | if str == base[:len(str)] { |
344 | return | 349 | return |
345 | } | 350 | } |
346 | t, err = time.Parse(timeFormat[:len(str)], str) | 351 | t, err = time.Parse(timeFormat[:len(str)], str) |
347 | default: | 352 | default: |
348 | err = fmt.Errorf("invalid time string: %s", str) | 353 | err = fmt.Errorf("invalid time string: %s", str) |
349 | return | 354 | return |
350 | } | 355 | } |
351 | 356 | ||
352 | // Adjust location | 357 | // Adjust location |
353 | if err == nil && loc != time.UTC { | 358 | if err == nil && loc != time.UTC { |
354 | y, mo, d := t.Date() | 359 | y, mo, d := t.Date() |
355 | h, mi, s := t.Clock() | 360 | h, mi, s := t.Clock() |
356 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil | 361 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil |
357 | } | 362 | } |
358 | 363 | ||
359 | return | 364 | return |
360 | } | 365 | } |
361 | 366 | ||
362 | // NullDate ... | 367 | // NullDate ... |
363 | type NullDate struct { | 368 | type NullDate struct { |
364 | Time time.Time | 369 | Time time.Time |
365 | Valid bool // Valid is true if Time is not NULL | 370 | Valid bool // Valid is true if Time is not NULL |
366 | } | 371 | } |
367 | 372 | ||
368 | // Scan ... | 373 | // Scan ... |
369 | func (nt *NullDate) Scan(value interface{}) (err error) { | 374 | func (nt *NullDate) Scan(value interface{}) (err error) { |
370 | if value == nil { | 375 | if value == nil { |
371 | nt.Time, nt.Valid = time.Time{}, false | 376 | nt.Time, nt.Valid = time.Time{}, false |
372 | return | 377 | return |
373 | } | 378 | } |
374 | 379 | ||
375 | switch v := value.(type) { | 380 | switch v := value.(type) { |
376 | case time.Time: | 381 | case time.Time: |
377 | nt.Time, nt.Valid = v, true | 382 | nt.Time, nt.Valid = v, true |
378 | return | 383 | return |
379 | case []byte: | 384 | case []byte: |
380 | nt.Time, err = parseSQLDate(string(v), time.UTC) | 385 | nt.Time, err = parseSQLDate(string(v), time.UTC) |
381 | nt.Valid = (err == nil) | 386 | nt.Valid = (err == nil) |
382 | return | 387 | return |
383 | case string: | 388 | case string: |
384 | nt.Time, err = parseSQLDate(v, time.UTC) | 389 | nt.Time, err = parseSQLDate(v, time.UTC) |
385 | nt.Valid = (err == nil) | 390 | nt.Valid = (err == nil) |
386 | return | 391 | return |
387 | } | 392 | } |
388 | 393 | ||
389 | nt.Valid = false | 394 | nt.Valid = false |
390 | return fmt.Errorf("Can't convert %T to time.Time", value) | 395 | return fmt.Errorf("Can't convert %T to time.Time", value) |
391 | } | 396 | } |
392 | 397 | ||
393 | // Value implements the driver Valuer interface. | 398 | // Value implements the driver Valuer interface. |
394 | func (nt NullDate) Value() (driver.Value, error) { | 399 | func (nt NullDate) Value() (driver.Value, error) { |
395 | if !nt.Valid { | 400 | if !nt.Valid { |
396 | return nil, nil | 401 | return nil, nil |
397 | } | 402 | } |
398 | return nt.Time, nil | 403 | return nt.Time, nil |
399 | } | 404 | } |
400 | 405 | ||
401 | // MarshalJSON ... | 406 | // MarshalJSON ... |
402 | func (nt NullDate) MarshalJSON() ([]byte, error) { | 407 | func (nt NullDate) MarshalJSON() ([]byte, error) { |
403 | if nt.Valid { | 408 | if nt.Valid { |
404 | format := nt.Time.Format("2006-01-02") | 409 | format := nt.Time.Format("2006-01-02") |
405 | return json.Marshal(format) | 410 | return json.Marshal(format) |
406 | } | 411 | } |
407 | return json.Marshal(nil) | 412 | return json.Marshal(nil) |
408 | } | 413 | } |
409 | 414 | ||
410 | // UnmarshalJSON ... | 415 | // UnmarshalJSON ... |
411 | func (nt *NullDate) UnmarshalJSON(b []byte) error { | 416 | func (nt *NullDate) UnmarshalJSON(b []byte) error { |
412 | var temp *time.Time | 417 | var temp *time.Time |
413 | var t1 time.Time | 418 | var t1 time.Time |
414 | var err error | 419 | var err error |
415 | 420 | ||
416 | s1 := string(b) | 421 | s1 := string(b) |
417 | s2 := s1[1 : len(s1)-1] | 422 | s2 := s1[1 : len(s1)-1] |
418 | if s1 == "null" { | 423 | if s1 == "null" { |
419 | temp = nil | 424 | temp = nil |
420 | } else { | 425 | } else { |
421 | t1, err = time.Parse("2006-01-02", s2) | 426 | t1, err = time.Parse("2006-01-02", s2) |
422 | if err != nil { | 427 | if err != nil { |
423 | return err | 428 | return err |
424 | } | 429 | } |
425 | temp = &t1 | 430 | temp = &t1 |
426 | } | 431 | } |
427 | 432 | ||
428 | if temp != nil { | 433 | if temp != nil { |
429 | nt.Scan(t1) | 434 | nt.Scan(t1) |
430 | } else { | 435 | } else { |
431 | nt.Valid = false | 436 | nt.Valid = false |
432 | } | 437 | } |
433 | return nil | 438 | return nil |
434 | } | 439 | } |
435 | 440 | ||
436 | func (nt *NullDate) CastToSQL() NullDate { | 441 | func (nt *NullDate) CastToSQL() NullDate { |
437 | return *nt | 442 | return *nt |
438 | } | 443 | } |
439 | 444 | ||
445 | func (nd *NullDate) Format(f string) string { | ||
446 | if !nd.Valid { | ||
447 | return "" | ||
448 | } | ||
449 | |||
450 | return EpochToDate(nd.Time.Unix(), f) | ||
451 | } | ||
452 | |||
440 | func parseSQLDate(str string, loc *time.Location) (t time.Time, err error) { | 453 | func parseSQLDate(str string, loc *time.Location) (t time.Time, err error) { |
441 | base := "0000-00-00" | 454 | base := "0000-00-00" |
442 | timeFormat := "2006-01-02" | 455 | timeFormat := "2006-01-02" |
443 | switch len(str) { | 456 | switch len(str) { |
444 | case 10: | 457 | case 10: |
445 | if str == base[:len(str)] { | 458 | if str == base[:len(str)] { |
446 | return | 459 | return |
447 | } | 460 | } |
448 | t, err = time.Parse(timeFormat[:len(str)], str) | 461 | t, err = time.Parse(timeFormat[:len(str)], str) |
449 | default: | 462 | default: |
450 | err = fmt.Errorf("invalid time string: %s", str) | 463 | err = fmt.Errorf("invalid time string: %s", str) |
451 | return | 464 | return |
452 | } | 465 | } |
453 | 466 | ||
454 | // Adjust location | 467 | // Adjust location |
455 | if err == nil && loc != time.UTC { | 468 | if err == nil && loc != time.UTC { |
456 | y, mo, d := t.Date() | 469 | y, mo, d := t.Date() |
457 | h, mi, s := t.Clock() | 470 | h, mi, s := t.Clock() |
458 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil | 471 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil |
459 | } | 472 | } |
460 | 473 | ||
461 | return | 474 | return |
462 | } | 475 | } |
463 | 476 | ||
464 | // NullTime ... | 477 | // NullTime ... |
465 | type NullTime struct { | 478 | type NullTime struct { |
466 | Time time.Time | 479 | Time time.Time |
467 | Valid bool // Valid is true if Time is not NULL | 480 | Valid bool // Valid is true if Time is not NULL |
468 | } | 481 | } |
469 | 482 | ||
470 | // Scan ... | 483 | // Scan ... |
471 | func (nt *NullTime) Scan(value interface{}) (err error) { | 484 | func (nt *NullTime) Scan(value interface{}) (err error) { |
472 | if value == nil { | 485 | if value == nil { |
473 | nt.Time, nt.Valid = time.Time{}, false | 486 | nt.Time, nt.Valid = time.Time{}, false |
474 | return | 487 | return |
475 | } | 488 | } |
476 | 489 | ||
477 | switch v := value.(type) { | 490 | switch v := value.(type) { |
478 | case time.Time: | 491 | case time.Time: |
479 | nt.Time, nt.Valid = v, true | 492 | nt.Time, nt.Valid = v, true |
480 | return | 493 | return |
481 | case []byte: | 494 | case []byte: |
482 | nt.Time, err = parseSQLTime(string(v), time.UTC) | 495 | nt.Time, err = parseSQLTime(string(v), time.UTC) |
483 | nt.Valid = (err == nil) | 496 | nt.Valid = (err == nil) |
484 | return | 497 | return |
485 | case string: | 498 | case string: |
486 | nt.Time, err = parseSQLTime(v, time.UTC) | 499 | nt.Time, err = parseSQLTime(v, time.UTC) |
487 | nt.Valid = (err == nil) | 500 | nt.Valid = (err == nil) |
488 | return | 501 | return |
489 | } | 502 | } |
490 | 503 | ||
491 | nt.Valid = false | 504 | nt.Valid = false |
492 | return fmt.Errorf("Can't convert %T to time.Time", value) | 505 | return fmt.Errorf("Can't convert %T to time.Time", value) |
493 | } | 506 | } |
494 | 507 | ||
495 | // Value implements the driver Valuer interface. | 508 | // Value implements the driver Valuer interface. |
496 | func (nt NullTime) Value() (driver.Value, error) { | 509 | func (nt NullTime) Value() (driver.Value, error) { |
497 | if !nt.Valid { | 510 | if !nt.Valid { |
498 | return nil, nil | 511 | return nil, nil |
499 | } | 512 | } |
500 | return nt.Time, nil | 513 | return nt.Time, nil |
501 | } | 514 | } |
502 | 515 | ||
503 | // MarshalJSON ... | 516 | // MarshalJSON ... |
504 | func (nt NullTime) MarshalJSON() ([]byte, error) { | 517 | func (nt NullTime) MarshalJSON() ([]byte, error) { |
505 | if nt.Valid { | 518 | if nt.Valid { |
506 | format := nt.Time.Format("15:04:05") | 519 | format := nt.Time.Format("15:04:05") |
507 | return json.Marshal(format) | 520 | return json.Marshal(format) |
508 | } | 521 | } |
509 | return json.Marshal(nil) | 522 | return json.Marshal(nil) |
510 | } | 523 | } |
511 | 524 | ||
512 | // UnmarshalJSON ... | 525 | // UnmarshalJSON ... |
513 | func (nt *NullTime) UnmarshalJSON(b []byte) error { | 526 | func (nt *NullTime) UnmarshalJSON(b []byte) error { |
514 | var temp *time.Time | 527 | var temp *time.Time |
515 | var t1 time.Time | 528 | var t1 time.Time |
516 | var err error | 529 | var err error |
517 | 530 | ||
518 | s1 := string(b) | 531 | s1 := string(b) |
519 | s2 := s1[1 : len(s1)-1] | 532 | s2 := s1[1 : len(s1)-1] |
520 | if s1 == "null" { | 533 | if s1 == "null" { |
521 | temp = nil | 534 | temp = nil |
522 | } else { | 535 | } else { |
523 | t1, err = time.Parse("2006-05-04 15:04:05", "1970-01-01 "+s2) | 536 | t1, err = time.Parse("2006-05-04 15:04:05", "1970-01-01 "+s2) |
524 | if err != nil { | 537 | if err != nil { |
525 | return err | 538 | return err |
526 | } | 539 | } |
527 | temp = &t1 | 540 | temp = &t1 |
528 | } | 541 | } |
529 | 542 | ||
530 | if temp != nil { | 543 | if temp != nil { |
531 | nt.Scan(t1) | 544 | nt.Scan(t1) |
532 | } else { | 545 | } else { |
533 | nt.Valid = false | 546 | nt.Valid = false |
534 | } | 547 | } |
535 | return nil | 548 | return nil |
536 | } | 549 | } |
537 | 550 | ||
538 | func (nt *NullTime) CastToSQL() NullTime { | 551 | func (nt *NullTime) CastToSQL() NullTime { |
539 | return *nt | 552 | return *nt |
540 | } | 553 | } |
541 | 554 | ||
542 | // NOTE(marko): Date must be included because database can't convert it to TIME otherwise. | 555 | // NOTE(marko): Date must be included because database can't convert it to TIME otherwise. |
543 | func parseSQLTime(str string, loc *time.Location) (t time.Time, err error) { | 556 | func parseSQLTime(str string, loc *time.Location) (t time.Time, err error) { |
544 | base := "00:00:00" | 557 | base := "00:00:00" |
545 | timeFormat := "15:04:05" | 558 | timeFormat := "15:04:05" |
546 | switch len(str) { | 559 | switch len(str) { |
547 | case 8: | 560 | case 8: |
548 | if str == base[:len(str)] { | 561 | if str == base[:len(str)] { |
549 | return | 562 | return |
550 | } | 563 | } |
551 | t, err = time.Parse("2006-05-04 "+timeFormat[:len(str)], "1970-01-01 "+str) | 564 | t, err = time.Parse("2006-05-04 "+timeFormat[:len(str)], "1970-01-01 "+str) |
552 | default: | 565 | default: |
553 | err = fmt.Errorf("invalid time string: %s", str) | 566 | err = fmt.Errorf("invalid time string: %s", str) |
554 | return | 567 | return |
555 | } | 568 | } |
556 | 569 | ||
557 | // Adjust location | 570 | // Adjust location |
558 | if err == nil && loc != time.UTC { | 571 | if err == nil && loc != time.UTC { |
559 | y, mo, d := t.Date() | 572 | y, mo, d := t.Date() |
560 | h, mi, s := t.Clock() | 573 | h, mi, s := t.Clock() |
561 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil | 574 | t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil |
562 | } | 575 | } |
563 | 576 | ||
564 | return | 577 | return |
565 | } | 578 | } |
566 | 579 |
server.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "database/sql" | 4 | "database/sql" |
5 | "fmt" | 5 | "fmt" |
6 | "net/http" | 6 | "net/http" |
7 | "time" | ||
7 | 8 | ||
8 | "git.to-net.rs/marko.tikvic/gologger" | 9 | "git.to-net.rs/marko.tikvic/gologger" |
9 | "github.com/gorilla/mux" | 10 | "github.com/gorilla/mux" |
10 | ) | 11 | ) |
11 | 12 | ||
12 | type Server struct { | 13 | type Server struct { |
13 | DB *sql.DB | 14 | DB *sql.DB |
14 | Router *mux.Router | 15 | Router *mux.Router |
15 | Logger *gologger.Logger | 16 | Logger *gologger.Logger |
16 | Port string | 17 | Port string |
17 | UTCOffset int64 | 18 | DBs map[string]*sql.DB |
18 | DBs map[string]*sql.DB | 19 | dsn map[string]string |
19 | } | 20 | } |
20 | 21 | ||
21 | func NewODBCServer(dsn, port, logDir string, utcOffset int64) (s *Server, err error) { | 22 | func NewODBCServer(dsn, port, logDir string) (s *Server, err error) { |
22 | s = new(Server) | 23 | s = new(Server) |
23 | 24 | ||
24 | s.Port = port | 25 | s.Port = port |
25 | 26 | ||
26 | if s.DB, err = sql.Open("odbc", fmt.Sprintf("DSN=%s;", dsn)); err != nil { | 27 | if s.DB, err = sql.Open("odbc", fmt.Sprintf("DSN=%s;", dsn)); err != nil { |
27 | return nil, err | 28 | return nil, err |
28 | } | 29 | } |
29 | 30 | ||
30 | s.Router = mux.NewRouter() | 31 | s.Router = mux.NewRouter() |
31 | 32 | ||
32 | if s.Logger, err = gologger.New("err", logDir, gologger.MaxLogSize1MB); err != nil { | 33 | if s.Logger, err = gologger.New("err", logDir, gologger.MaxLogSize1MB); err != nil { |
33 | return nil, fmt.Errorf("can't create logger: %s", err.Error()) | 34 | return nil, fmt.Errorf("can't create logger: %s", err.Error()) |
34 | } | 35 | } |
35 | 36 | ||
36 | s.UTCOffset = utcOffset | ||
37 | |||
38 | s.DBs = make(map[string]*sql.DB) | 37 | s.DBs = make(map[string]*sql.DB) |
39 | s.DBs["default"] = s.DB | 38 | s.DBs["default"] = s.DB |
40 | 39 | ||
40 | s.dsn = make(map[string]string) | ||
41 | s.DBs["default"] = s.DB | ||
42 | |||
41 | return s, nil | 43 | return s, nil |
42 | } | 44 | } |
43 | 45 | ||
44 | func (s *Server) Run() { | 46 | func (s *Server) Run() { |
45 | s.Logger.Print("Server listening on %s", s.Port) | 47 | s.Logger.Print("Server listening on %s", s.Port) |
46 | s.Logger.PrintAndTrace(http.ListenAndServe(s.Port, s.Router).Error()) | 48 | s.Logger.PrintAndTrace(http.ListenAndServe(s.Port, s.Router).Error()) |
47 | } | 49 | } |
48 | 50 | ||
49 | func (s *Server) Cleanup() { | 51 | func (s *Server) Cleanup() { |
50 | if s.DB != nil { | 52 | if s.DB != nil { |
51 | s.DB.Close() | 53 | s.DB.Close() |
52 | } | 54 | } |
53 | 55 | ||
54 | if s.Logger != nil { | 56 | if s.Logger != nil { |
55 | s.Logger.Close() | 57 | s.Logger.Close() |
56 | } | 58 | } |
57 | } | 59 | } |
58 | 60 | ||
59 | func (s *Server) StartTransaction() (*sql.Tx, error) { | 61 | func (s *Server) StartTransaction() (*sql.Tx, error) { |
60 | return s.DB.Begin() | 62 | return s.DB.Begin() |
61 | } | 63 | } |
62 | 64 | ||
63 | func CommitChanges(tx *sql.Tx, err *error, opt ...error) { | 65 | func CommitChanges(tx *sql.Tx, err *error, opt ...error) { |
64 | if *err != nil { | 66 | if *err != nil { |
65 | tx.Rollback() | 67 | tx.Rollback() |
66 | return | 68 | return |
67 | } | 69 | } |
68 | 70 | ||
69 | for _, e := range opt { | 71 | for _, e := range opt { |
70 | if e != nil { | 72 | if e != nil { |
71 | tx.Rollback() | 73 | tx.Rollback() |
72 | return | 74 | return |
73 | } | 75 | } |
74 | } | 76 | } |
75 | 77 | ||
76 | if *err = tx.Commit(); *err != nil { | 78 | if *err = tx.Commit(); *err != nil { |
77 | tx.Rollback() | 79 | tx.Rollback() |
78 | } | 80 | } |
79 | } | 81 | } |
82 | |||
83 | func (s *Server) RefreshDatabaseConnections(period time.Duration) { | ||
84 | for { | ||
85 | for k, db := range s.DBs { | ||
86 | if err := db.Ping(); err != nil { | ||
87 | if s.Logger != nil { | ||
88 | s.Logger.PrintAndTrace("failed to ping database (%s): %s", s.dsn[k], err.Error()) | ||
89 | } else { | ||
90 | fmt.Println("failed to ping database (%s): %s", s.dsn[k], err.Error()) | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | time.Sleep(period) | ||
95 | } |
string_util.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "strconv" | 5 | "strconv" |
6 | "strings" | 6 | "strings" |
7 | "unicode" | 7 | "unicode" |
8 | |||
9 | "golang.org/x/exp/utf8string" | ||
10 | ) | 8 | ) |
11 | 9 | ||
12 | const sanitisationPatern = "\"';&*<>=\\`:" | 10 | const sanitisationPatern = "\"';&*<>=\\`:" |
13 | 11 | ||
14 | // SanitiseString removes characters from s found in patern and returns new modified string. | 12 | // SanitiseString removes characters from s found in patern and returns new modified string. |
15 | func SanitiseString(s string) string { | 13 | func SanitiseString(s string) string { |
16 | return ReplaceAny(s, sanitisationPatern, "") | 14 | return ReplaceAny(s, sanitisationPatern, "") |
17 | } | 15 | } |
18 | 16 | ||
19 | // IsWrappedWith ... | 17 | // IsWrappedWith ... |
20 | func IsWrappedWith(src, begin, end string) bool { | 18 | func IsWrappedWith(src, begin, end string) bool { |
21 | return strings.HasPrefix(src, begin) && strings.HasSuffix(src, end) | 19 | return strings.HasPrefix(src, begin) && strings.HasSuffix(src, end) |
22 | } | 20 | } |
23 | 21 | ||
24 | // ParseInt64Arr ... | 22 | // ParseInt64Arr ... |
25 | func ParseInt64Arr(s, sep string) (arr []int64) { | 23 | func ParseInt64Arr(s, sep string) (arr []int64) { |
26 | s = strings.TrimSpace(s) | 24 | s = strings.TrimSpace(s) |
27 | if s == "" { | 25 | if s == "" { |
28 | return | 26 | return |
29 | } | 27 | } |
30 | parts := strings.Split(s, sep) | 28 | parts := strings.Split(s, sep) |
31 | arr = make([]int64, len(parts)) | 29 | arr = make([]int64, len(parts)) |
32 | for i, p := range parts { | 30 | for i, p := range parts { |
33 | num := StringToInt64(p) | 31 | num := StringToInt64(p) |
34 | arr[i] = num | 32 | arr[i] = num |
35 | } | 33 | } |
36 | 34 | ||
37 | return arr | 35 | return arr |
38 | } | 36 | } |
39 | 37 | ||
40 | // Int64SliceToString ... | 38 | // Int64SliceToString ... |
41 | func Int64SliceToString(arr []int64) (s string) { | 39 | func Int64SliceToString(arr []int64) (s string) { |
42 | if len(arr) == 0 { | 40 | if len(arr) == 0 { |
43 | return "" | 41 | return "" |
44 | } | 42 | } |
45 | 43 | ||
46 | s += fmt.Sprintf("%d", arr[0]) | 44 | s += fmt.Sprintf("%d", arr[0]) |
47 | for i := 1; i < len(arr); i++ { | 45 | for i := 1; i < len(arr); i++ { |
48 | s += fmt.Sprintf(",%d", arr[i]) | 46 | s += fmt.Sprintf(",%d", arr[i]) |
49 | } | 47 | } |
50 | 48 | ||
51 | return s | 49 | return s |
52 | } | 50 | } |
53 | 51 | ||
54 | // CombineStrings ... | 52 | // CombineStrings ... |
55 | func CombineStrings(s1, s2, s3 string) string { | 53 | func CombineStrings(s1, s2, s3 string) string { |
56 | s1 = strings.TrimSpace(s1) | 54 | s1 = strings.TrimSpace(s1) |
57 | s2 = strings.TrimSpace(s2) | 55 | s2 = strings.TrimSpace(s2) |
58 | 56 | ||
59 | if s1 != "" && s2 != "" { | 57 | if s1 != "" && s2 != "" { |
60 | s1 += s3 + s2 | 58 | s1 += s3 + s2 |
61 | } else { | 59 | } else { |
62 | s1 += s2 | 60 | s1 += s2 |
63 | } | 61 | } |
64 | 62 | ||
65 | return s1 | 63 | return s1 |
66 | } | 64 | } |
67 | 65 | ||
68 | // ReplaceAny replaces any of the characters from patern found in s with r and returns a new resulting string. | 66 | // ReplaceAny replaces any of the characters from patern found in s with r and returns a new resulting string. |
69 | func ReplaceAny(s, patern, r string) (n string) { | 67 | func ReplaceAny(s, patern, r string) (n string) { |
70 | n = s | 68 | n = s |
71 | for _, c := range patern { | 69 | for _, c := range patern { |
72 | n = strings.Replace(n, string(c), r, -1) | 70 | n = strings.Replace(n, string(c), r, -1) |
73 | } | 71 | } |
74 | return n | 72 | return n |
75 | } | 73 | } |
76 | 74 | ||
77 | // StringToBool ... | 75 | // StringToBool ... |
78 | func StringToBool(s string) bool { | 76 | func StringToBool(s string) bool { |
79 | res, _ := strconv.ParseBool(s) | 77 | res, _ := strconv.ParseBool(s) |
80 | return res | 78 | return res |
81 | } | 79 | } |
82 | 80 | ||
83 | // BoolToString ... | 81 | // BoolToString ... |
84 | func BoolToString(b bool) string { | 82 | func BoolToString(b bool) string { |
85 | return fmt.Sprintf("%b", b) | 83 | return fmt.Sprintf("%b", b) |
86 | } | 84 | } |
87 | 85 | ||
88 | // StringSliceContains ... | 86 | // StringSliceContains ... |
89 | func StringSliceContains(slice []string, s string) bool { | 87 | func StringSliceContains(slice []string, s string) bool { |
90 | for i := range slice { | 88 | for i := range slice { |
91 | if slice[i] == s { | 89 | if slice[i] == s { |
92 | return true | 90 | return true |
93 | } | 91 | } |
94 | } | 92 | } |
95 | return false | 93 | return false |
96 | } | 94 | } |
97 | 95 | ||
98 | func SplitString(s, sep string) (res []string) { | 96 | func SplitString(s, sep string) (res []string) { |
99 | parts := strings.Split(s, sep) | 97 | parts := strings.Split(s, sep) |
100 | for _, p := range parts { | 98 | for _, p := range parts { |
101 | if p != "" { | 99 | if p != "" { |
102 | res = append(res, p) | 100 | res = append(res, p) |
103 | } | 101 | } |
104 | } | 102 | } |
105 | return res | 103 | return res |
106 | } | 104 | } |
107 | 105 | ||
108 | // StringAt ... | 106 | // StringAt ... |
109 | func StringAt(s string, index int) string { | 107 | func StringAt(s string, index int) string { |
110 | if len(s)-1 < index || index < 0 { | 108 | if len(s)-1 < index || index < 0 { |
111 | return "" | 109 | return "" |
112 | } | 110 | } |
113 | 111 | ||
114 | return string(s[index]) | 112 | return string(s[index]) |
115 | } | 113 | } |
116 | 114 | ||
117 | func StringAtRune(s string, index int) string { | 115 | func StringAtRune(s string, index int) string { |
118 | str := utf8string.NewString(s) | 116 | str := []rune(s) |
119 | max := str.RuneCount() | 117 | if index < len(str) { |
120 | if index < max { | 118 | return string(str[index]) |
121 | return string(str.At(index)) | ||
122 | } | 119 | } |
123 | return "" | 120 | return "" |
124 | } | 121 | } |
125 | 122 | ||
126 | // SplitText ... | 123 | // SplitText ... |
127 | func SplitText(s string, maxLen int) (lines []string) { | 124 | func SplitText(s string, maxLen int) (lines []string) { |
128 | runes := []rune(s) | 125 | runes := []rune(s) |
129 | 126 | ||
130 | i, start, sep, l := 0, 0, 0, 0 | 127 | i, start, sep, l := 0, 0, 0, 0 |
131 | for i = 0; i < len(runes); i++ { | 128 | for i = 0; i < len(runes); i++ { |
132 | c := runes[i] | 129 | c := runes[i] |
133 | 130 | ||
134 | if unicode.IsSpace(c) { | 131 | if unicode.IsSpace(c) { |
135 | sep = i | 132 | sep = i |
136 | } | 133 | } |
137 | 134 | ||
138 | if c == '\n' { | 135 | if c == '\n' { |
139 | if start != sep { | 136 | if start != sep { |
140 | lines = append(lines, string(runes[start:sep])) | 137 | lines = append(lines, string(runes[start:sep])) |
141 | } | 138 | } |
142 | start = i | 139 | start = i |
143 | sep = i | 140 | sep = i |
144 | l = 0 | 141 | l = 0 |
145 | } else if l >= maxLen { | 142 | } else if l >= maxLen { |
146 | if start != sep { | 143 | if start != sep { |
147 | lines = append(lines, string(runes[start:sep])) | 144 | lines = append(lines, string(runes[start:sep])) |
148 | sep = i | 145 | sep = i |
149 | start = i - 1 | 146 | start = i - 1 |
150 | l = 0 | 147 | l = 0 |
151 | } | 148 | } |
152 | } else { | 149 | } else { |
153 | l++ | 150 | l++ |
154 | } | 151 | } |
155 | } | 152 | } |
156 | if start != i-1 { | 153 | if start != i-1 { |
157 | lines = append(lines, string(runes[start:i-1])) | 154 | lines = append(lines, string(runes[start:i-1])) |
158 | } | 155 | } |
159 | 156 | ||
160 | return lines | 157 | return lines |
161 | } | 158 | } |
162 | 159 | ||
163 | func CutTextWith(txt string, maxLen int, tail string) string { | 160 | func CutTextWith(txt string, maxLen int, tail string) string { |
164 | if len(txt) < maxLen || len(txt) <= len(tail) { | 161 | if len(txt) < maxLen || len(txt) <= len(tail) { |
165 | return txt | 162 | return txt |
166 | } | 163 | } |
167 | 164 | ||
168 | return txt[:maxLen-3] + tail | 165 | return txt[:maxLen-3] + tail |
169 | } | 166 | } |
170 | 167 | ||
171 | func LimitTextWith(txt string, maxLen int, tail string) string { | 168 | func LimitTextWith(txt string, maxLen int, tail string) string { |
172 | if len(txt) <= maxLen { | 169 | if len(txt) <= maxLen { |
173 | return txt | 170 | return txt |
174 | } | 171 | } |
175 | 172 | ||
176 | return txt[:maxLen] + tail | 173 | return txt[:maxLen] + tail |
177 | } | 174 | } |
178 | 175 | ||
179 | // SplitStringAtWholeWords ... | 176 | // SplitStringAtWholeWords ... |
180 | func SplitStringAtWholeWords(s string, maxLen int) (res []string) { | 177 | func SplitStringAtWholeWords(s string, maxLen int) (res []string) { |
181 | parts := strings.Split(s, " ") | 178 | parts := strings.Split(s, " ") |
182 | 179 | ||
183 | res = append(res, parts[0]) | 180 | res = append(res, parts[0]) |
184 | i := 0 | 181 | i := 0 |
185 | for j := 1; j < len(parts); j++ { | 182 | for j := 1; j < len(parts); j++ { |
186 | p := strings.TrimSpace(parts[j]) | 183 | p := strings.TrimSpace(parts[j]) |
187 | if len(p) > maxLen { | 184 | if len(p) > maxLen { |
188 | // TODO(marko): check if maxLen is >= 3 | 185 | // TODO(marko): check if maxLen is >= 3 |
189 | p = p[0 : maxLen-3] | 186 | p = p[0 : maxLen-3] |
190 | p += "..." | 187 | p += "..." |
191 | } | 188 | } |
192 | if len(res[i])+len(p)+1 <= maxLen { | 189 | if len(res[i])+len(p)+1 <= maxLen { |
193 | res[i] += " " + p | 190 | res[i] += " " + p |
194 | } else { | 191 | } else { |
195 | res = append(res, p) | 192 | res = append(res, p) |
196 | i++ | 193 | i++ |
197 | } | 194 | } |
198 | } | 195 | } |
199 | 196 | ||
200 | return res | 197 | return res |
201 | } | 198 | } |
202 | 199 | ||
203 | // StringToInt64 ... | 200 | // StringToInt64 ... |
204 | func StringToInt64(s string) int64 { | 201 | func StringToInt64(s string) int64 { |
205 | i, _ := strconv.ParseInt(s, 10, 64) | 202 | i, _ := strconv.ParseInt(s, 10, 64) |
206 | return i | 203 | return i |
207 | } | 204 | } |
208 | 205 | ||
209 | // StringToFloat64 ... | 206 | // StringToFloat64 ... |
210 | func StringToFloat64(s string) float64 { | 207 | func StringToFloat64(s string) float64 { |
211 | f, _ := strconv.ParseFloat(s, 64) | 208 | f, _ := strconv.ParseFloat(s, 64) |
212 | return f | 209 | return f |
213 | } | 210 | } |
214 | 211 | ||
215 | func StringToValidInt64(s string) (int64, bool) { | 212 | func StringToValidInt64(s string) (int64, bool) { |
216 | i, err := strconv.ParseInt(s, 10, 64) | 213 | i, err := strconv.ParseInt(s, 10, 64) |
217 | if err != nil { | 214 | if err != nil { |
218 | return i, false | 215 | return i, false |
219 | } | 216 | } |
220 | return i, true | 217 | return i, true |
221 | } | 218 | } |
222 | 219 |