-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
flag_timestamp.go
152 lines (125 loc) · 3.38 KB
/
flag_timestamp.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
package cli
import (
"errors"
"fmt"
"time"
)
type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue]
// TimestampConfig defines the config for timestamp flags
type TimestampConfig struct {
Timezone *time.Location
// Available layouts for flag value.
//
// Note that value for formats with missing year/date will be interpreted as current year/date respectively.
//
// Read more about time layouts: https://pkg.go.dev/time#pkg-constants
Layouts []string
}
// timestampValue wrap to satisfy golang's flag interface.
type timestampValue struct {
timestamp *time.Time
hasBeenSet bool
layouts []string
location *time.Location
}
var _ ValueCreator[time.Time, TimestampConfig] = timestampValue{}
// Below functions are to satisfy the ValueCreator interface
func (t timestampValue) Create(val time.Time, p *time.Time, c TimestampConfig) Value {
*p = val
return ×tampValue{
timestamp: p,
layouts: c.Layouts,
location: c.Timezone,
}
}
func (t timestampValue) ToString(b time.Time) string {
if b.IsZero() {
return ""
}
return fmt.Sprintf("%v", b)
}
// Timestamp constructor(for internal testing only)
func newTimestamp(timestamp time.Time) *timestampValue {
return ×tampValue{timestamp: ×tamp}
}
// Below functions are to satisfy the flag.Value interface
// Parses the string value to timestamp
func (t *timestampValue) Set(value string) error {
var timestamp time.Time
var err error
if t.location == nil {
t.location = time.UTC
}
if len(t.layouts) == 0 {
return errors.New("got nil/empty layouts slice")
}
for _, layout := range t.layouts {
var locErr error
timestamp, locErr = time.ParseInLocation(layout, value, t.location)
if locErr != nil {
if err == nil {
err = locErr
continue
}
err = newMultiError(err, locErr)
continue
}
err = nil
break
}
if err != nil {
return err
}
defaultTS, _ := time.ParseInLocation(time.TimeOnly, time.TimeOnly, timestamp.Location())
n := time.Now()
// If format is missing date (or year only), set it explicitly to current
if timestamp.Truncate(time.Hour*24).UnixNano() == defaultTS.Truncate(time.Hour*24).UnixNano() {
timestamp = time.Date(
n.Year(),
n.Month(),
n.Day(),
timestamp.Hour(),
timestamp.Minute(),
timestamp.Second(),
timestamp.Nanosecond(),
timestamp.Location(),
)
} else if timestamp.Year() == 0 {
timestamp = time.Date(
n.Year(),
timestamp.Month(),
timestamp.Day(),
timestamp.Hour(),
timestamp.Minute(),
timestamp.Second(),
timestamp.Nanosecond(),
timestamp.Location(),
)
}
if t.timestamp != nil {
*t.timestamp = timestamp
}
t.hasBeenSet = true
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (t *timestampValue) String() string {
return fmt.Sprintf("%#v", t.timestamp)
}
// Value returns the timestamp value stored in the flag
func (t *timestampValue) Value() *time.Time {
return t.timestamp
}
// Get returns the flag structure
func (t *timestampValue) Get() any {
return *t.timestamp
}
// Timestamp gets the timestamp from a flag name
func (cmd *Command) Timestamp(name string) time.Time {
if v, ok := cmd.Value(name).(time.Time); ok {
tracef("time.Time available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name)
return v
}
tracef("time.Time NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name)
return time.Time{}
}