|
@@ -0,0 +1,183 @@
|
|
1
|
+// Copyright 2010 The Go Authors. All rights reserved.
|
|
2
|
+
|
|
3
|
+package custime
|
|
4
|
+
|
|
5
|
+import (
|
|
6
|
+ "errors"
|
|
7
|
+ "time"
|
|
8
|
+)
|
|
9
|
+
|
|
10
|
+// see https://github.com/golang/go/blob/7ad512e7ffe576c4894ea84b02e954846fbda643/src/time/format.go#L1251
|
|
11
|
+
|
|
12
|
+// This is a forked version of the ParseDuration function that also handles days/months/years
|
|
13
|
+
|
|
14
|
+var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
|
|
15
|
+
|
|
16
|
+// leadingInt consumes the leading [0-9]* from s.
|
|
17
|
+func leadingInt(s string) (x int64, rem string, err error) {
|
|
18
|
+ i := 0
|
|
19
|
+ for ; i < len(s); i++ {
|
|
20
|
+ c := s[i]
|
|
21
|
+ if c < '0' || c > '9' {
|
|
22
|
+ break
|
|
23
|
+ }
|
|
24
|
+ if x > (1<<63-1)/10 {
|
|
25
|
+ // overflow
|
|
26
|
+ return 0, "", errLeadingInt
|
|
27
|
+ }
|
|
28
|
+ x = x*10 + int64(c) - '0'
|
|
29
|
+ if x < 0 {
|
|
30
|
+ // overflow
|
|
31
|
+ return 0, "", errLeadingInt
|
|
32
|
+ }
|
|
33
|
+ }
|
|
34
|
+ return x, s[i:], nil
|
|
35
|
+}
|
|
36
|
+
|
|
37
|
+// leadingFraction consumes the leading [0-9]* from s.
|
|
38
|
+// It is used only for fractions, so does not return an error on overflow,
|
|
39
|
+// it just stops accumulating precision.
|
|
40
|
+func leadingFraction(s string) (x int64, scale float64, rem string) {
|
|
41
|
+ i := 0
|
|
42
|
+ scale = 1
|
|
43
|
+ overflow := false
|
|
44
|
+ for ; i < len(s); i++ {
|
|
45
|
+ c := s[i]
|
|
46
|
+ if c < '0' || c > '9' {
|
|
47
|
+ break
|
|
48
|
+ }
|
|
49
|
+ if overflow {
|
|
50
|
+ continue
|
|
51
|
+ }
|
|
52
|
+ if x > (1<<63-1)/10 {
|
|
53
|
+ // It's possible for overflow to give a positive number, so take care.
|
|
54
|
+ overflow = true
|
|
55
|
+ continue
|
|
56
|
+ }
|
|
57
|
+ y := x*10 + int64(c) - '0'
|
|
58
|
+ if y < 0 {
|
|
59
|
+ overflow = true
|
|
60
|
+ continue
|
|
61
|
+ }
|
|
62
|
+ x = y
|
|
63
|
+ scale *= 10
|
|
64
|
+ }
|
|
65
|
+ return x, scale, s[i:]
|
|
66
|
+}
|
|
67
|
+
|
|
68
|
+var unitMap = map[string]int64{
|
|
69
|
+ "ns": int64(time.Nanosecond),
|
|
70
|
+ "us": int64(time.Microsecond),
|
|
71
|
+ "µs": int64(time.Microsecond), // U+00B5 = micro symbol
|
|
72
|
+ "μs": int64(time.Microsecond), // U+03BC = Greek letter mu
|
|
73
|
+ "ms": int64(time.Millisecond),
|
|
74
|
+ "s": int64(time.Second),
|
|
75
|
+ "m": int64(time.Minute),
|
|
76
|
+ "h": int64(time.Hour),
|
|
77
|
+ "d": int64(time.Hour * 24),
|
|
78
|
+ "mo": int64(time.Hour * 24 * 30),
|
|
79
|
+ "y": int64(time.Hour * 24 * 265),
|
|
80
|
+}
|
|
81
|
+
|
|
82
|
+// ParseDuration parses a duration string.
|
|
83
|
+// A duration string is a possibly signed sequence of
|
|
84
|
+// decimal numbers, each with optional fraction and a unit suffix,
|
|
85
|
+// such as "300ms", "-1.5h" or "2h45m".
|
|
86
|
+// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
|
87
|
+func ParseDuration(s string) (time.Duration, error) {
|
|
88
|
+ // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
|
|
89
|
+ orig := s
|
|
90
|
+ var d int64
|
|
91
|
+ neg := false
|
|
92
|
+
|
|
93
|
+ // Consume [-+]?
|
|
94
|
+ if s != "" {
|
|
95
|
+ c := s[0]
|
|
96
|
+ if c == '-' || c == '+' {
|
|
97
|
+ neg = c == '-'
|
|
98
|
+ s = s[1:]
|
|
99
|
+ }
|
|
100
|
+ }
|
|
101
|
+ // Special case: if all that is left is "0", this is zero.
|
|
102
|
+ if s == "0" {
|
|
103
|
+ return 0, nil
|
|
104
|
+ }
|
|
105
|
+ if s == "" {
|
|
106
|
+ return 0, errors.New("time: invalid duration " + orig)
|
|
107
|
+ }
|
|
108
|
+ for s != "" {
|
|
109
|
+ var (
|
|
110
|
+ v, f int64 // integers before, after decimal point
|
|
111
|
+ scale float64 = 1 // value = v + f/scale
|
|
112
|
+ )
|
|
113
|
+
|
|
114
|
+ var err error
|
|
115
|
+
|
|
116
|
+ // The next character must be [0-9.]
|
|
117
|
+ if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
|
|
118
|
+ return 0, errors.New("time: invalid duration " + orig)
|
|
119
|
+ }
|
|
120
|
+ // Consume [0-9]*
|
|
121
|
+ pl := len(s)
|
|
122
|
+ v, s, err = leadingInt(s)
|
|
123
|
+ if err != nil {
|
|
124
|
+ return 0, errors.New("time: invalid duration " + orig)
|
|
125
|
+ }
|
|
126
|
+ pre := pl != len(s) // whether we consumed anything before a period
|
|
127
|
+
|
|
128
|
+ // Consume (\.[0-9]*)?
|
|
129
|
+ post := false
|
|
130
|
+ if s != "" && s[0] == '.' {
|
|
131
|
+ s = s[1:]
|
|
132
|
+ pl := len(s)
|
|
133
|
+ f, scale, s = leadingFraction(s)
|
|
134
|
+ post = pl != len(s)
|
|
135
|
+ }
|
|
136
|
+ if !pre && !post {
|
|
137
|
+ // no digits (e.g. ".s" or "-.s")
|
|
138
|
+ return 0, errors.New("time: invalid duration " + orig)
|
|
139
|
+ }
|
|
140
|
+
|
|
141
|
+ // Consume unit.
|
|
142
|
+ i := 0
|
|
143
|
+ for ; i < len(s); i++ {
|
|
144
|
+ c := s[i]
|
|
145
|
+ if c == '.' || '0' <= c && c <= '9' {
|
|
146
|
+ break
|
|
147
|
+ }
|
|
148
|
+ }
|
|
149
|
+ if i == 0 {
|
|
150
|
+ return 0, errors.New("time: missing unit in duration " + orig)
|
|
151
|
+ }
|
|
152
|
+ u := s[:i]
|
|
153
|
+ s = s[i:]
|
|
154
|
+ unit, ok := unitMap[u]
|
|
155
|
+ if !ok {
|
|
156
|
+ return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
|
|
157
|
+ }
|
|
158
|
+ if v > (1<<63-1)/unit {
|
|
159
|
+ // overflow
|
|
160
|
+ return 0, errors.New("time: invalid duration " + orig)
|
|
161
|
+ }
|
|
162
|
+ v *= unit
|
|
163
|
+ if f > 0 {
|
|
164
|
+ // float64 is needed to be nanosecond accurate for fractions of hours.
|
|
165
|
+ // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
|
|
166
|
+ v += int64(float64(f) * (float64(unit) / scale))
|
|
167
|
+ if v < 0 {
|
|
168
|
+ // overflow
|
|
169
|
+ return 0, errors.New("time: invalid duration " + orig)
|
|
170
|
+ }
|
|
171
|
+ }
|
|
172
|
+ d += v
|
|
173
|
+ if d < 0 {
|
|
174
|
+ // overflow
|
|
175
|
+ return 0, errors.New("time: invalid duration " + orig)
|
|
176
|
+ }
|
|
177
|
+ }
|
|
178
|
+
|
|
179
|
+ if neg {
|
|
180
|
+ d = -d
|
|
181
|
+ }
|
|
182
|
+ return time.Duration(d), nil
|
|
183
|
+}
|