1
2
3
4
5
6
7
8
9
10
11
12
13
14 package model
15
16 import (
17 "encoding/json"
18 "errors"
19 "fmt"
20 "math"
21 "strconv"
22 "strings"
23 "time"
24 )
25
26 const (
27
28
29 minimumTick = time.Millisecond
30
31 second = int64(time.Second / minimumTick)
32
33 nanosPerTick = int64(minimumTick / time.Nanosecond)
34
35
36
37 Earliest = Time(math.MinInt64)
38
39
40 Latest = Time(math.MaxInt64)
41 )
42
43
44
45 type Time int64
46
47
48 type Interval struct {
49 Start, End Time
50 }
51
52
53 func Now() Time {
54 return TimeFromUnixNano(time.Now().UnixNano())
55 }
56
57
58
59 func TimeFromUnix(t int64) Time {
60 return Time(t * second)
61 }
62
63
64
65 func TimeFromUnixNano(t int64) Time {
66 return Time(t / nanosPerTick)
67 }
68
69
70 func (t Time) Equal(o Time) bool {
71 return t == o
72 }
73
74
75 func (t Time) Before(o Time) bool {
76 return t < o
77 }
78
79
80 func (t Time) After(o Time) bool {
81 return t > o
82 }
83
84
85 func (t Time) Add(d time.Duration) Time {
86 return t + Time(d/minimumTick)
87 }
88
89
90 func (t Time) Sub(o Time) time.Duration {
91 return time.Duration(t-o) * minimumTick
92 }
93
94
95 func (t Time) Time() time.Time {
96 return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
97 }
98
99
100
101 func (t Time) Unix() int64 {
102 return int64(t) / second
103 }
104
105
106
107 func (t Time) UnixNano() int64 {
108 return int64(t) * nanosPerTick
109 }
110
111
112 var dotPrecision = int(math.Log10(float64(second)))
113
114
115 func (t Time) String() string {
116 return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
117 }
118
119
120 func (t Time) MarshalJSON() ([]byte, error) {
121 return []byte(t.String()), nil
122 }
123
124
125 func (t *Time) UnmarshalJSON(b []byte) error {
126 p := strings.Split(string(b), ".")
127 switch len(p) {
128 case 1:
129 v, err := strconv.ParseInt(string(p[0]), 10, 64)
130 if err != nil {
131 return err
132 }
133 *t = Time(v * second)
134
135 case 2:
136 v, err := strconv.ParseInt(string(p[0]), 10, 64)
137 if err != nil {
138 return err
139 }
140 v *= second
141
142 prec := dotPrecision - len(p[1])
143 if prec < 0 {
144 p[1] = p[1][:dotPrecision]
145 } else if prec > 0 {
146 p[1] = p[1] + strings.Repeat("0", prec)
147 }
148
149 va, err := strconv.ParseInt(p[1], 10, 32)
150 if err != nil {
151 return err
152 }
153
154
155
156 if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 {
157 *t = Time(v+va) * -1
158 } else {
159 *t = Time(v + va)
160 }
161
162 default:
163 return fmt.Errorf("invalid time %q", string(b))
164 }
165 return nil
166 }
167
168
169
170
171 type Duration time.Duration
172
173
174 func (d *Duration) Set(s string) error {
175 var err error
176 *d, err = ParseDuration(s)
177 return err
178 }
179
180
181 func (d *Duration) Type() string {
182 return "duration"
183 }
184
185 func isdigit(c byte) bool { return c >= '0' && c <= '9' }
186
187
188
189 var unitMap = map[string]struct {
190 pos int
191 mult uint64
192 }{
193 "ms": {7, uint64(time.Millisecond)},
194 "s": {6, uint64(time.Second)},
195 "m": {5, uint64(time.Minute)},
196 "h": {4, uint64(time.Hour)},
197 "d": {3, uint64(24 * time.Hour)},
198 "w": {2, uint64(7 * 24 * time.Hour)},
199 "y": {1, uint64(365 * 24 * time.Hour)},
200 }
201
202
203
204 func ParseDuration(s string) (Duration, error) {
205 switch s {
206 case "0":
207
208 return 0, nil
209 case "":
210 return 0, errors.New("empty duration string")
211 }
212
213 orig := s
214 var dur uint64
215 lastUnitPos := 0
216
217 for s != "" {
218 if !isdigit(s[0]) {
219 return 0, fmt.Errorf("not a valid duration string: %q", orig)
220 }
221
222 i := 0
223 for ; i < len(s) && isdigit(s[i]); i++ {
224 }
225 v, err := strconv.ParseUint(s[:i], 10, 0)
226 if err != nil {
227 return 0, fmt.Errorf("not a valid duration string: %q", orig)
228 }
229 s = s[i:]
230
231
232 for i = 0; i < len(s) && !isdigit(s[i]); i++ {
233 }
234 if i == 0 {
235 return 0, fmt.Errorf("not a valid duration string: %q", orig)
236 }
237 u := s[:i]
238 s = s[i:]
239 unit, ok := unitMap[u]
240 if !ok {
241 return 0, fmt.Errorf("unknown unit %q in duration %q", u, orig)
242 }
243 if unit.pos <= lastUnitPos {
244 return 0, fmt.Errorf("not a valid duration string: %q", orig)
245 }
246 lastUnitPos = unit.pos
247
248 if v > 1<<63/unit.mult {
249 return 0, errors.New("duration out of range")
250 }
251 dur += v * unit.mult
252 if dur > 1<<63-1 {
253 return 0, errors.New("duration out of range")
254 }
255 }
256 return Duration(dur), nil
257 }
258
259 func (d Duration) String() string {
260 var (
261 ms = int64(time.Duration(d) / time.Millisecond)
262 r = ""
263 )
264 if ms == 0 {
265 return "0s"
266 }
267
268 f := func(unit string, mult int64, exact bool) {
269 if exact && ms%mult != 0 {
270 return
271 }
272 if v := ms / mult; v > 0 {
273 r += fmt.Sprintf("%d%s", v, unit)
274 ms -= v * mult
275 }
276 }
277
278
279
280 f("y", 1000*60*60*24*365, true)
281 f("w", 1000*60*60*24*7, true)
282
283 f("d", 1000*60*60*24, false)
284 f("h", 1000*60*60, false)
285 f("m", 1000*60, false)
286 f("s", 1000, false)
287 f("ms", 1, false)
288
289 return r
290 }
291
292
293 func (d Duration) MarshalJSON() ([]byte, error) {
294 return json.Marshal(d.String())
295 }
296
297
298 func (d *Duration) UnmarshalJSON(bytes []byte) error {
299 var s string
300 if err := json.Unmarshal(bytes, &s); err != nil {
301 return err
302 }
303 dur, err := ParseDuration(s)
304 if err != nil {
305 return err
306 }
307 *d = dur
308 return nil
309 }
310
311
312 func (d *Duration) MarshalText() ([]byte, error) {
313 return []byte(d.String()), nil
314 }
315
316
317 func (d *Duration) UnmarshalText(text []byte) error {
318 var err error
319 *d, err = ParseDuration(string(text))
320 return err
321 }
322
323
324 func (d Duration) MarshalYAML() (interface{}, error) {
325 return d.String(), nil
326 }
327
328
329 func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
330 var s string
331 if err := unmarshal(&s); err != nil {
332 return err
333 }
334 dur, err := ParseDuration(s)
335 if err != nil {
336 return err
337 }
338 *d = dur
339 return nil
340 }
341
View as plain text