-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathrelative_time_flag.go
More file actions
159 lines (129 loc) · 4.45 KB
/
relative_time_flag.go
File metadata and controls
159 lines (129 loc) · 4.45 KB
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
155
156
157
158
159
package pflags
import (
"fmt"
"strconv"
"time"
"github.com/spf13/pflag"
)
const (
RelativeTimeFlagType = "relative-time"
)
type Clock interface {
Now() time.Time
}
type FixedTimeClock struct {
now time.Time
}
// NewTestClock is used for setting a specific point in time for testing purposes.
func NewTestClock(now int64) Clock {
return &FixedTimeClock{
now: time.UnixMilli(now),
}
}
// This clock is useful when parsing multiple time flags. In that case you do not want
// a clock that still ticks, because the two or more flags could both be in relative time
// and we should not want a situation where they both flags are relative to a moving target.
func NewFixedTimeClock() Clock {
return &FixedTimeClock{
now: time.Now(),
}
}
func (clock *FixedTimeClock) Now() time.Time {
return clock.now
}
// RelativeTimeValue adapts time.Time for use as a flag. It can be specified as a relative time or absolute time
type RelativeTimeValue struct {
clock Clock
*time.Time
}
var _ pflag.DefaultZeroProvider = (*RelativeTimeValue)(nil)
func newRelativeTimeValue(val string, p *time.Time, clock Clock) *RelativeTimeValue {
v, err := ParseRelativeTime(clock, val)
if err != nil {
panic(err)
}
*p = v
tv := &RelativeTimeValue{
clock: clock,
Time: p,
}
return tv
}
func (d *RelativeTimeValue) IsDefaultZero(defValue string) bool {
return defValue == "" || durationZeroRegex.MatchString(defValue)
}
func ParseRelativeTime(clock Clock, s string) (time.Time, error) {
if len(s) > 0 && s[0] == '-' {
d, err := ParseDuration(s[1:])
if err != nil {
return time.Time{}, newInvalidRelativeTimeError(s, err)
}
result := clock.Now().Add(-d)
return result, nil
}
unixTime, err := strconv.ParseInt(s, 0, 64) //nolint:mnd
if err != nil {
result, err := time.Parse(time.RFC3339, s)
if err != nil {
return time.Time{}, newTimeParseError(s)
}
return result, nil
}
return time.UnixMilli(unixTime), nil
}
func newTimeParseError(strTime string) error {
return fmt.Errorf(
"invalid time format \"%s\"."+
" Expected an ISO8601 date/time string (e.g. 2016-09-13T13:12:04Z), milliseconds since the unix epoch (e.g. 1652210038107) or relative time (e.g. -168h)",
strTime,
)
}
func newInvalidRelativeTimeError(strTime string, durationErr error) error {
return fmt.Errorf(
"invalid relative time format \"%s\" - %s", strTime, durationErr,
)
}
// Set time.Time value from string based on accepted formats.
func (d *RelativeTimeValue) Set(s string) error {
v, err := ParseRelativeTime(d.clock, s)
*d.Time = v
return err
}
// Type name for time.Time flags.
func (d *RelativeTimeValue) Type() string {
return RelativeTimeFlagType
}
func (d *RelativeTimeValue) String() string { return d.Format(time.RFC3339Nano) }
// GetRelativeTime return the time value of a flag with the given name
func GetRelativeTime(f *pflag.FlagSet, name string) (time.Time, error) {
val, err := GetFlagType(f, name, RelativeTimeFlagType, func(f *pflag.Flag, sval string) (interface{}, error) {
clock := f.Value.(*RelativeTimeValue).clock
return ParseRelativeTime(clock, sval)
})
if err != nil {
return time.Time{}, err
}
return val.(time.Time), nil
}
// RelativeTimeVar defines a time.Time flag with specified name, default value, and usage string.
// The argument p points to a time.Time variable in which to store the value of the flag.
func RelativeTimeVar(f *pflag.FlagSet, p *time.Time, name string, value string, clock Clock, usage string) {
f.Var(newRelativeTimeValue(value, p, clock), name, usage)
}
// RelativeTimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
func RelativeTimeVarP(f *pflag.FlagSet, p *time.Time, name, shorthand string, value string, clock Clock, usage string) {
f.VarP(newRelativeTimeValue(value, p, clock), name, shorthand, usage)
}
// RelativeTime defines a time.Time flag with specified name, default value, and usage string.
// The return value is the address of a time.Time variable that stores the value of the flag.
func RelativeTime(f *pflag.FlagSet, name string, value string, clock Clock, usage string) *time.Time {
p := new(time.Time)
RelativeTimeVarP(f, p, name, "", value, clock, usage)
return p
}
// RelativeTimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
func RelativeTimeP(f *pflag.FlagSet, name, shorthand string, value string, clock Clock, usage string) *time.Time {
p := new(time.Time)
RelativeTimeVarP(f, p, name, shorthand, value, clock, usage)
return p
}