-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathduration.go
142 lines (108 loc) · 2.95 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
/*
Package duration parses RFC3339 duration strings into time.Duration
Installation
Just go get the package:
go get -u github.com/peterhellberg/duration
Usage
A small usage example
package main
import (
"fmt"
"github.com/peterhellberg/duration"
)
func main() {
if d, err := duration.Parse("P1DT30H4S"); err == nil {
fmt.Println(d) // Output: 54h0m4s
}
}
*/
package duration
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
const (
// HoursPerDay is the number of hours per day according to Google
HoursPerDay = 24.0
// HoursPerWeek is the number of hours per week according to Google
HoursPerWeek = 168.0
// HoursPerMonth is the number of hours per month according to Google
HoursPerMonth = 730.4841667
// HoursPerYear is the number of hours per year according to Google
HoursPerYear = 8765.81
)
var (
// ErrInvalidString is returned when passed an invalid string
ErrInvalidString = fmt.Errorf("invalid duration string")
// ErrUnsupportedFormat is returned when parsing fails
ErrUnsupportedFormat = fmt.Errorf("unsupported duration string format")
pattern = regexp.MustCompile(`\A(-)?P((?P<years>[\d\.]+)Y)?((?P<months>[\d\.]+)M)?((?P<weeks>[\d\.]+)W)?((?P<days>[\d\.]+)D)?(T((?P<hours>[\d\.]+)H)?((?P<minutes>[\d\.]+)M)?((?P<seconds>[\d\.]+?)S)?)?\z`)
invalidStrings = []string{"", "P", "PT"}
)
// Parse a RFC3339 duration string into time.Duration
func Parse(s string) (time.Duration, error) {
if contains(invalidStrings, s) || strings.HasSuffix(s, "T") {
return 0, ErrInvalidString
}
var (
match []string
prefix string
)
if pattern.MatchString(s) {
match = pattern.FindStringSubmatch(s)
} else {
return 0, ErrUnsupportedFormat
}
if strings.HasPrefix(s, "-") {
prefix = "-"
}
return durationFromMatchAndPrefix(match, prefix)
}
func durationFunc(prefix string) func(string, float64) time.Duration {
return func(format string, f float64) time.Duration {
if d, err := time.ParseDuration(fmt.Sprintf(prefix+format, f)); err == nil {
return d
}
return time.Duration(0)
}
}
func durationFromMatchAndPrefix(match []string, prefix string) (time.Duration, error) {
d := time.Duration(0)
duration := durationFunc(prefix)
for i, name := range pattern.SubexpNames() {
value := match[i]
if i == 0 || name == "" || value == "" {
continue
}
if f, err := strconv.ParseFloat(value, 64); err == nil {
switch name {
case "years":
d += duration("%fh", f*HoursPerYear)
case "months":
d += duration("%fh", f*HoursPerMonth)
case "weeks":
d += duration("%fh", f*HoursPerWeek)
case "days":
d += duration("%fh", f*HoursPerDay)
case "hours":
d += duration("%fh", f)
case "minutes":
d += duration("%fm", f)
case "seconds":
d += duration("%fs", f)
}
}
}
return d, nil
}
func contains(slice []string, item string) bool {
set := make(map[string]struct{}, len(slice))
for _, s := range slice {
set[s] = struct{}{}
}
_, ok := set[item]
return ok
}