forked from raintank/dur
-
Notifications
You must be signed in to change notification settings - Fork 0
/
duration.go
154 lines (142 loc) · 4.08 KB
/
duration.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Package dur is a package to convert string duration and time specifications
// to numbers of seconds and to unix timestamps.
// It aims to support the full specification as outlined in http://graphite.readthedocs.io/en/latest/render_api.html#from-until
// which incorporates the formats defined by `at`.
//
// this package works with the following shorthands:
// Duration : unsigned (positive) number of seconds
// NDuration: like Duration, but non-zero.
package dur
import (
"errors"
"fmt"
"strconv"
)
var errEmpty = errors.New("number cannot be empty")
var errNegative = errors.New("number cannot be negative")
var errNonZero = errors.New("number must be nonzero")
var errUnknownTimeUnit = errors.New("unknown time unit")
// MustParseNDuration parses a format string to a non-zero number of seconds, or panics otherwise
// unit defaults to s if not specified
func MustParseNDuration(desc, s string) uint32 {
sec, err := ParseNDuration(s)
if err != nil {
panic(fmt.Sprintf("%q: %s", desc, s))
}
return sec
}
// MustParseDuration parses a format string to a number of seconds, or panics otherwise
// unit defaults to s if not specified
func MustParseDuration(desc, s string) uint32 {
sec, err := ParseDuration(s)
if err != nil {
panic(fmt.Sprintf("%q: %s", desc, s))
}
return sec
}
// ParseNDuration parses a format string to a non-zero number of seconds, or error otherwise
// unit defaults to s if not specified
func ParseNDuration(s string) (uint32, error) {
i, e := ParseDuration(s)
if e == nil && i == 0 {
return 0, errNonZero
}
return i, e
}
const (
sMulti = 1
mMulti = 60
hMulti = 60 * 60
dMulti = 60 * 60 * 24
wMulti = 60 * 60 * 24 * 7
monMulti = 60 * 60 * 24 * 30
yMulti = 60 * 60 * 24 * 365
)
// ParseDuration parses a format string to a number of seconds, or error otherwise
// valid units are s/sec/secs/second/seconds, m/min/mins/minute/minutes, h/hour/hours, d/day/days, w/week/weeks, mon/month/months, y/year/years
// unit defaults to s if not specified
func ParseDuration(s string) (uint32, error) {
if s == "" {
return 0, errEmpty
}
if s[0] == '-' {
return 0, errNegative
}
var sum uint32
for len(s) > 0 {
var i int
for i < len(s) && '0' <= s[i] && s[i] <= '9' {
i++
}
var numStr string
numStr, s = s[:i], s[i:]
i = 0
for i < len(s) && (s[i] < '0' || '9' < s[i]) {
i++
}
var unitStr string
unitStr, s = s[:i], s[i:]
var units int
switch unitStr {
case "", "s", "sec", "secs", "second", "seconds":
units = sMulti
case "m", "min", "mins", "minute", "minutes":
units = mMulti
case "h", "hour", "hours":
units = hMulti
case "d", "day", "days":
units = dMulti
case "w", "week", "weeks":
units = wMulti
case "mon", "month", "months":
units = monMulti
case "y", "year", "years":
units = yMulti
default:
return 0, errUnknownTimeUnit
}
num, err := strconv.Atoi(numStr)
if err != nil {
return 0, err
}
sum += uint32(num * units)
}
return sum, nil
}
// FormatDuration takes a number of seconds and returns a minimal string represention
// that can be parsed by ParseDuration(). Due to its ambiguity and length, we don't
// use "month" units in the returned value.
func FormatDuration(seconds uint32) string {
output := ""
if seconds == 0 {
return "0s"
}
var numUnits uint32
for seconds > 0 {
if seconds >= yMulti {
numUnits = seconds / yMulti
seconds = seconds - (numUnits * yMulti)
output += fmt.Sprintf("%dy", numUnits)
} else if seconds >= wMulti {
numUnits = seconds / wMulti
seconds = seconds - (numUnits * wMulti)
output += fmt.Sprintf("%dw", numUnits)
} else if seconds >= dMulti {
numUnits = seconds / dMulti
seconds = seconds - (numUnits * dMulti)
output += fmt.Sprintf("%dd", numUnits)
} else if seconds >= hMulti {
numUnits = seconds / hMulti
seconds = seconds - (numUnits * hMulti)
output += fmt.Sprintf("%dh", numUnits)
} else if seconds >= mMulti {
numUnits = seconds / mMulti
seconds = seconds - (numUnits * mMulti)
output += fmt.Sprintf("%dm", numUnits)
} else {
output += fmt.Sprintf("%ds", seconds)
seconds = 0
}
}
return output
}