-
Notifications
You must be signed in to change notification settings - Fork 0
/
time.go
145 lines (128 loc) · 3.99 KB
/
time.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
package main
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
// These are additional predefined layouts for use in Time.Format and Time.Parse
// with --since and --until parameters for `docker logs` and `docker events`
const (
rFC3339Local = "2006-01-02T15:04:05" // RFC3339 with local timezone
rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone
dateWithZone = "2006-01-02Z07:00" // RFC3339 with time at 00:00:00
dateLocal = "2006-01-02" // RFC3339 with local timezone and time at 00:00:00
)
var relativeRe = regexp.MustCompile(`(\d+)\s?(m|min|mins|minute|minutes|h|hour|hours|d|day|days|w|week|weeks)(?: ago)?`)
func parseTime(value string) (time.Time, error) {
m := relativeRe.FindAllStringSubmatch(strings.TrimSpace(value), 1)
if m != nil {
t, err := parseRelativeTime(m[0], time.Now())
if err == nil {
return t, err
}
// if err, try other parse
}
switch value {
case "all", "ALL":
return time.Unix(0, 0), nil
case "", "now":
return time.Now(), nil
}
return getTimestamp(value, time.Now())
}
// This files is comes from moby's code
// https://github.com/moby/moby/blob/3a633a712c8bbb863fe7e57ec132dd87a9c4eff7/api/types/time/timestamp.go
// GetTimestamp tries to parse given string as golang duration,
// then RFC3339 time and finally as a Unix timestamp. If
// any of these were successful, it returns a Unix timestamp
// as string otherwise returns the given value back.
// In case of duration input, the returned timestamp is computed
// as the given reference time minus the amount of the duration.
func getTimestamp(value string, reference time.Time) (time.Time, error) {
if d, err := time.ParseDuration(value); value != "0" && err == nil {
return reference.Add(-d), nil
}
var format string
// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
if strings.Contains(value, ".") {
if parseInLocation {
format = rFC3339NanoLocal
} else {
format = time.RFC3339Nano
}
} else if strings.Contains(value, "T") {
// we want the number of colons in the T portion of the timestamp
tcolons := strings.Count(value, ":")
// if parseInLocation is off and we have a +/- zone offset (not Z) then
// there will be an extra colon in the input for the tz offset subtract that
// colon from the tcolons count
if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 {
tcolons--
}
if parseInLocation {
switch tcolons {
case 0:
format = "2006-01-02T15"
case 1:
format = "2006-01-02T15:04"
default:
format = rFC3339Local
}
} else {
switch tcolons {
case 0:
format = "2006-01-02T15Z07:00"
case 1:
format = "2006-01-02T15:04Z07:00"
default:
format = time.RFC3339
}
}
} else if parseInLocation {
format = dateLocal
} else {
format = dateWithZone
}
var t time.Time
var err error
if parseInLocation {
t, err = time.ParseInLocation(format, value, time.FixedZone(reference.Zone()))
} else {
t, err = time.Parse(format, value)
}
if err != nil {
if strings.Contains(value, "-") {
return time.Unix(0, 0), err // was probably an RFC3339 like timestamp but the parser failed with an error
}
intVal, err := strconv.Atoi(value)
if err != nil {
return time.Unix(0, 0), err
}
t = time.Unix(int64(intVal), 0)
}
return t, nil
}
func parseRelativeTime(m []string, t time.Time) (time.Time, error) {
if len(m) != 3 {
return t, fmt.Errorf("invalid Relative Time")
}
p, err := strconv.Atoi(m[1])
if err != nil {
return t, err
}
var d time.Duration
switch m[2] {
case "m", "min", "mins", "minute", "minutes":
d = -time.Duration(int(p)) * time.Minute
case "h", "hour", "hours":
d = -time.Duration(int(p)) * time.Hour
case "d", "day", "days":
d = -time.Duration(int(p)) * 24 * time.Hour
case "w", "week", "weeks":
d = -time.Duration(int(p)) * 24 * time.Hour * 7
}
return t.Add(d), nil
}