1 | // Copyright 2013 The Prometheus Authors
|
---|
2 | // Licensed under the Apache License, Version 2.0 (the "License");
|
---|
3 | // you may not use this file except in compliance with the License.
|
---|
4 | // You may obtain a copy of the License at
|
---|
5 | //
|
---|
6 | // http://www.apache.org/licenses/LICENSE-2.0
|
---|
7 | //
|
---|
8 | // Unless required by applicable law or agreed to in writing, software
|
---|
9 | // distributed under the License is distributed on an "AS IS" BASIS,
|
---|
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
11 | // See the License for the specific language governing permissions and
|
---|
12 | // limitations under the License.
|
---|
13 |
|
---|
14 | package model
|
---|
15 |
|
---|
16 | import (
|
---|
17 | "encoding/json"
|
---|
18 | "errors"
|
---|
19 | "fmt"
|
---|
20 | "math"
|
---|
21 | "regexp"
|
---|
22 | "strconv"
|
---|
23 | "strings"
|
---|
24 | "time"
|
---|
25 | )
|
---|
26 |
|
---|
27 | const (
|
---|
28 | // MinimumTick is the minimum supported time resolution. This has to be
|
---|
29 | // at least time.Second in order for the code below to work.
|
---|
30 | minimumTick = time.Millisecond
|
---|
31 | // second is the Time duration equivalent to one second.
|
---|
32 | second = int64(time.Second / minimumTick)
|
---|
33 | // The number of nanoseconds per minimum tick.
|
---|
34 | nanosPerTick = int64(minimumTick / time.Nanosecond)
|
---|
35 |
|
---|
36 | // Earliest is the earliest Time representable. Handy for
|
---|
37 | // initializing a high watermark.
|
---|
38 | Earliest = Time(math.MinInt64)
|
---|
39 | // Latest is the latest Time representable. Handy for initializing
|
---|
40 | // a low watermark.
|
---|
41 | Latest = Time(math.MaxInt64)
|
---|
42 | )
|
---|
43 |
|
---|
44 | // Time is the number of milliseconds since the epoch
|
---|
45 | // (1970-01-01 00:00 UTC) excluding leap seconds.
|
---|
46 | type Time int64
|
---|
47 |
|
---|
48 | // Interval describes an interval between two timestamps.
|
---|
49 | type Interval struct {
|
---|
50 | Start, End Time
|
---|
51 | }
|
---|
52 |
|
---|
53 | // Now returns the current time as a Time.
|
---|
54 | func Now() Time {
|
---|
55 | return TimeFromUnixNano(time.Now().UnixNano())
|
---|
56 | }
|
---|
57 |
|
---|
58 | // TimeFromUnix returns the Time equivalent to the Unix Time t
|
---|
59 | // provided in seconds.
|
---|
60 | func TimeFromUnix(t int64) Time {
|
---|
61 | return Time(t * second)
|
---|
62 | }
|
---|
63 |
|
---|
64 | // TimeFromUnixNano returns the Time equivalent to the Unix Time
|
---|
65 | // t provided in nanoseconds.
|
---|
66 | func TimeFromUnixNano(t int64) Time {
|
---|
67 | return Time(t / nanosPerTick)
|
---|
68 | }
|
---|
69 |
|
---|
70 | // Equal reports whether two Times represent the same instant.
|
---|
71 | func (t Time) Equal(o Time) bool {
|
---|
72 | return t == o
|
---|
73 | }
|
---|
74 |
|
---|
75 | // Before reports whether the Time t is before o.
|
---|
76 | func (t Time) Before(o Time) bool {
|
---|
77 | return t < o
|
---|
78 | }
|
---|
79 |
|
---|
80 | // After reports whether the Time t is after o.
|
---|
81 | func (t Time) After(o Time) bool {
|
---|
82 | return t > o
|
---|
83 | }
|
---|
84 |
|
---|
85 | // Add returns the Time t + d.
|
---|
86 | func (t Time) Add(d time.Duration) Time {
|
---|
87 | return t + Time(d/minimumTick)
|
---|
88 | }
|
---|
89 |
|
---|
90 | // Sub returns the Duration t - o.
|
---|
91 | func (t Time) Sub(o Time) time.Duration {
|
---|
92 | return time.Duration(t-o) * minimumTick
|
---|
93 | }
|
---|
94 |
|
---|
95 | // Time returns the time.Time representation of t.
|
---|
96 | func (t Time) Time() time.Time {
|
---|
97 | return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
|
---|
98 | }
|
---|
99 |
|
---|
100 | // Unix returns t as a Unix time, the number of seconds elapsed
|
---|
101 | // since January 1, 1970 UTC.
|
---|
102 | func (t Time) Unix() int64 {
|
---|
103 | return int64(t) / second
|
---|
104 | }
|
---|
105 |
|
---|
106 | // UnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
---|
107 | // since January 1, 1970 UTC.
|
---|
108 | func (t Time) UnixNano() int64 {
|
---|
109 | return int64(t) * nanosPerTick
|
---|
110 | }
|
---|
111 |
|
---|
112 | // The number of digits after the dot.
|
---|
113 | var dotPrecision = int(math.Log10(float64(second)))
|
---|
114 |
|
---|
115 | // String returns a string representation of the Time.
|
---|
116 | func (t Time) String() string {
|
---|
117 | return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
|
---|
118 | }
|
---|
119 |
|
---|
120 | // MarshalJSON implements the json.Marshaler interface.
|
---|
121 | func (t Time) MarshalJSON() ([]byte, error) {
|
---|
122 | return []byte(t.String()), nil
|
---|
123 | }
|
---|
124 |
|
---|
125 | // UnmarshalJSON implements the json.Unmarshaler interface.
|
---|
126 | func (t *Time) UnmarshalJSON(b []byte) error {
|
---|
127 | p := strings.Split(string(b), ".")
|
---|
128 | switch len(p) {
|
---|
129 | case 1:
|
---|
130 | v, err := strconv.ParseInt(string(p[0]), 10, 64)
|
---|
131 | if err != nil {
|
---|
132 | return err
|
---|
133 | }
|
---|
134 | *t = Time(v * second)
|
---|
135 |
|
---|
136 | case 2:
|
---|
137 | v, err := strconv.ParseInt(string(p[0]), 10, 64)
|
---|
138 | if err != nil {
|
---|
139 | return err
|
---|
140 | }
|
---|
141 | v *= second
|
---|
142 |
|
---|
143 | prec := dotPrecision - len(p[1])
|
---|
144 | if prec < 0 {
|
---|
145 | p[1] = p[1][:dotPrecision]
|
---|
146 | } else if prec > 0 {
|
---|
147 | p[1] = p[1] + strings.Repeat("0", prec)
|
---|
148 | }
|
---|
149 |
|
---|
150 | va, err := strconv.ParseInt(p[1], 10, 32)
|
---|
151 | if err != nil {
|
---|
152 | return err
|
---|
153 | }
|
---|
154 |
|
---|
155 | // If the value was something like -0.1 the negative is lost in the
|
---|
156 | // parsing because of the leading zero, this ensures that we capture it.
|
---|
157 | if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 {
|
---|
158 | *t = Time(v+va) * -1
|
---|
159 | } else {
|
---|
160 | *t = Time(v + va)
|
---|
161 | }
|
---|
162 |
|
---|
163 | default:
|
---|
164 | return fmt.Errorf("invalid time %q", string(b))
|
---|
165 | }
|
---|
166 | return nil
|
---|
167 | }
|
---|
168 |
|
---|
169 | // Duration wraps time.Duration. It is used to parse the custom duration format
|
---|
170 | // from YAML.
|
---|
171 | // This type should not propagate beyond the scope of input/output processing.
|
---|
172 | type Duration time.Duration
|
---|
173 |
|
---|
174 | // Set implements pflag/flag.Value
|
---|
175 | func (d *Duration) Set(s string) error {
|
---|
176 | var err error
|
---|
177 | *d, err = ParseDuration(s)
|
---|
178 | return err
|
---|
179 | }
|
---|
180 |
|
---|
181 | // Type implements pflag.Value
|
---|
182 | func (d *Duration) Type() string {
|
---|
183 | return "duration"
|
---|
184 | }
|
---|
185 |
|
---|
186 | var durationRE = regexp.MustCompile("^(([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?$")
|
---|
187 |
|
---|
188 | // ParseDuration parses a string into a time.Duration, assuming that a year
|
---|
189 | // always has 365d, a week always has 7d, and a day always has 24h.
|
---|
190 | func ParseDuration(durationStr string) (Duration, error) {
|
---|
191 | switch durationStr {
|
---|
192 | case "0":
|
---|
193 | // Allow 0 without a unit.
|
---|
194 | return 0, nil
|
---|
195 | case "":
|
---|
196 | return 0, errors.New("empty duration string")
|
---|
197 | }
|
---|
198 | matches := durationRE.FindStringSubmatch(durationStr)
|
---|
199 | if matches == nil {
|
---|
200 | return 0, fmt.Errorf("not a valid duration string: %q", durationStr)
|
---|
201 | }
|
---|
202 | var dur time.Duration
|
---|
203 |
|
---|
204 | // Parse the match at pos `pos` in the regex and use `mult` to turn that
|
---|
205 | // into ms, then add that value to the total parsed duration.
|
---|
206 | var overflowErr error
|
---|
207 | m := func(pos int, mult time.Duration) {
|
---|
208 | if matches[pos] == "" {
|
---|
209 | return
|
---|
210 | }
|
---|
211 | n, _ := strconv.Atoi(matches[pos])
|
---|
212 |
|
---|
213 | // Check if the provided duration overflows time.Duration (> ~ 290years).
|
---|
214 | if n > int((1<<63-1)/mult/time.Millisecond) {
|
---|
215 | overflowErr = errors.New("duration out of range")
|
---|
216 | }
|
---|
217 | d := time.Duration(n) * time.Millisecond
|
---|
218 | dur += d * mult
|
---|
219 |
|
---|
220 | if dur < 0 {
|
---|
221 | overflowErr = errors.New("duration out of range")
|
---|
222 | }
|
---|
223 | }
|
---|
224 |
|
---|
225 | m(2, 1000*60*60*24*365) // y
|
---|
226 | m(4, 1000*60*60*24*7) // w
|
---|
227 | m(6, 1000*60*60*24) // d
|
---|
228 | m(8, 1000*60*60) // h
|
---|
229 | m(10, 1000*60) // m
|
---|
230 | m(12, 1000) // s
|
---|
231 | m(14, 1) // ms
|
---|
232 |
|
---|
233 | return Duration(dur), overflowErr
|
---|
234 | }
|
---|
235 |
|
---|
236 | func (d Duration) String() string {
|
---|
237 | var (
|
---|
238 | ms = int64(time.Duration(d) / time.Millisecond)
|
---|
239 | r = ""
|
---|
240 | )
|
---|
241 | if ms == 0 {
|
---|
242 | return "0s"
|
---|
243 | }
|
---|
244 |
|
---|
245 | f := func(unit string, mult int64, exact bool) {
|
---|
246 | if exact && ms%mult != 0 {
|
---|
247 | return
|
---|
248 | }
|
---|
249 | if v := ms / mult; v > 0 {
|
---|
250 | r += fmt.Sprintf("%d%s", v, unit)
|
---|
251 | ms -= v * mult
|
---|
252 | }
|
---|
253 | }
|
---|
254 |
|
---|
255 | // Only format years and weeks if the remainder is zero, as it is often
|
---|
256 | // easier to read 90d than 12w6d.
|
---|
257 | f("y", 1000*60*60*24*365, true)
|
---|
258 | f("w", 1000*60*60*24*7, true)
|
---|
259 |
|
---|
260 | f("d", 1000*60*60*24, false)
|
---|
261 | f("h", 1000*60*60, false)
|
---|
262 | f("m", 1000*60, false)
|
---|
263 | f("s", 1000, false)
|
---|
264 | f("ms", 1, false)
|
---|
265 |
|
---|
266 | return r
|
---|
267 | }
|
---|
268 |
|
---|
269 | // MarshalJSON implements the json.Marshaler interface.
|
---|
270 | func (d Duration) MarshalJSON() ([]byte, error) {
|
---|
271 | return json.Marshal(d.String())
|
---|
272 | }
|
---|
273 |
|
---|
274 | // UnmarshalJSON implements the json.Unmarshaler interface.
|
---|
275 | func (d *Duration) UnmarshalJSON(bytes []byte) error {
|
---|
276 | var s string
|
---|
277 | if err := json.Unmarshal(bytes, &s); err != nil {
|
---|
278 | return err
|
---|
279 | }
|
---|
280 | dur, err := ParseDuration(s)
|
---|
281 | if err != nil {
|
---|
282 | return err
|
---|
283 | }
|
---|
284 | *d = dur
|
---|
285 | return nil
|
---|
286 | }
|
---|
287 |
|
---|
288 | // MarshalText implements the encoding.TextMarshaler interface.
|
---|
289 | func (d *Duration) MarshalText() ([]byte, error) {
|
---|
290 | return []byte(d.String()), nil
|
---|
291 | }
|
---|
292 |
|
---|
293 | // UnmarshalText implements the encoding.TextUnmarshaler interface.
|
---|
294 | func (d *Duration) UnmarshalText(text []byte) error {
|
---|
295 | var err error
|
---|
296 | *d, err = ParseDuration(string(text))
|
---|
297 | return err
|
---|
298 | }
|
---|
299 |
|
---|
300 | // MarshalYAML implements the yaml.Marshaler interface.
|
---|
301 | func (d Duration) MarshalYAML() (interface{}, error) {
|
---|
302 | return d.String(), nil
|
---|
303 | }
|
---|
304 |
|
---|
305 | // UnmarshalYAML implements the yaml.Unmarshaler interface.
|
---|
306 | func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
---|
307 | var s string
|
---|
308 | if err := unmarshal(&s); err != nil {
|
---|
309 | return err
|
---|
310 | }
|
---|
311 | dur, err := ParseDuration(s)
|
---|
312 | if err != nil {
|
---|
313 | return err
|
---|
314 | }
|
---|
315 | *d = dur
|
---|
316 | return nil
|
---|
317 | }
|
---|