-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhumanTime.go
137 lines (124 loc) · 3.43 KB
/
humanTime.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
package gohumantime
import (
"errors"
"log"
"regexp"
"strconv"
"strings"
)
// Export Unit times in in milliseconds (easier testing)
const (
unitRegexpPattern = "(second|minute|hour|day|week|month|year)s?"
connectorRegexpPattern = "and|,"
Second = 1000
Minute = Second * 60
Hour = Minute * 60
Day = Hour * 24
Week = Day * 7
Month = Day * 30
Year = Day * 365
)
// humanTime holds internal data and methods to help converting a human readable time string into milliseconds
type humanTime struct {
languageMap map[string]string // Word to number map
unitRegexp *regexp.Regexp // Cached unit regex
connectorRegexp *regexp.Regexp // Cached connector regex
}
// processUnits converts time unit words like "minute" into the correct millisecond multiplier
func (h humanTime) processUnits(time string) (int, error) {
if strings.TrimSpace(time) == "" {
return 0, nil
}
fields := strings.Fields(time)
if len(fields) < 2 {
return 0, errors.New("No usable time literals found")
}
num, err := strconv.ParseFloat(fields[0], 10)
if err != nil {
num = 1
}
unit := h.unitRegexp.FindString(time)
var unitNum float64
switch unit {
case "second":
unitNum = Second
case "minute":
unitNum = Minute
case "hour":
unitNum = Hour
case "day":
unitNum = Day
case "week":
unitNum = Week
case "month":
unitNum = Month
case "year":
unitNum = Year
}
return int(unitNum * num), nil
}
// ToMilliseconds converts a human readable interval string into milliseconds.
// Example: ToMilliseconds("three minutes and five seconds") returns 3 * 60 * 1000 + 5 * 1000
func ToMilliseconds(humanReadableTime string) (int, error) {
return humanTime{map[string]string{
"now": "0",
"one": "1",
"two": "2",
"three": "3",
"four": "4",
"five": "5",
"six": "6",
"seven": "7",
"eight": "8",
"nine": "9",
"ten": "10",
"fifteen": "15",
"twenty": "20",
"thirty": "30",
"forty": "40",
"fifty": "50",
"sixty": "60",
"seventy": "70",
"eighty": "80",
"ninety": "90",
"hundred": "100",
},
regexp.MustCompile(unitRegexpPattern),
regexp.MustCompile(connectorRegexpPattern),
}.toMilliseconds(humanReadableTime)
}
// toMilliseconds converts a humanReadableTime string to milliseconds
func (h humanTime) toMilliseconds(humanReadableTime string) (sum int, err error) {
if ms, err := strconv.Atoi(humanReadableTime); err == nil {
// Easy way out - if already numeric, assume MS and return.
return ms, nil
}
defer func() {
if r := recover(); r != nil {
log.Println("Recovered panic in humanTime::ToMilliseconds", r)
err = errors.New("Malformed input")
}
}()
if strings.TrimSpace(humanReadableTime) == "" {
return 0, nil
}
timeString := h.wordNumbersToDecimals(strings.ToLower(humanReadableTime))
timeString = h.unitRegexp.ReplaceAllString(timeString, "$1,")
for _, s := range h.connectorRegexp.Split(timeString, -1) {
s, err := h.processUnits(s)
if err == nil {
sum += s
}
}
return
}
// wordNumbersToDecimals replaces word numbers like "one", "two" into numeric literals like "1", "2" etc
func (h humanTime) wordNumbersToDecimals(timeString string) string {
fields := strings.Fields(timeString)
for _, f := range fields {
if val, ok := h.languageMap[f]; ok {
timeString = strings.Replace(timeString, f, val, 1)
}
}
return timeString
}