Skip to content

Commit 7e4fa0a

Browse files
committed
Add support for time.Time flags
1 parent d5e0c06 commit 7e4fa0a

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

time.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package pflag
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
)
8+
9+
// TimeValue adapts time.Time for use as a flag.
10+
type TimeValue struct {
11+
*time.Time
12+
formats []string
13+
}
14+
15+
func newTimeValue(val time.Time, p *time.Time, formats []string) *TimeValue {
16+
*p = val
17+
return &TimeValue{
18+
Time: p,
19+
formats: formats,
20+
}
21+
}
22+
23+
// Set time.Time value from string based on accepted formats.
24+
func (d *TimeValue) Set(s string) error {
25+
s = strings.TrimSpace(s)
26+
for _, f := range d.formats {
27+
v, err := time.Parse(f, s)
28+
if err != nil {
29+
continue
30+
}
31+
*d.Time = v
32+
return nil
33+
}
34+
35+
formatsString := ""
36+
for i, f := range d.formats {
37+
if i > 0 {
38+
formatsString += ", "
39+
}
40+
formatsString += fmt.Sprintf("`%s`", f)
41+
}
42+
43+
return fmt.Errorf("invalid time format `%s` must be one of: %s", s, formatsString)
44+
}
45+
46+
// Type name for time.Time flags.
47+
func (d *TimeValue) Type() string {
48+
return "time"
49+
}
50+
51+
func (d *TimeValue) String() string { return d.Time.Format(time.RFC3339Nano) }
52+
53+
// GetTime return the time value of a flag with the given name
54+
func (f *FlagSet) GetTime(name string) (time.Time, error) {
55+
flag := f.Lookup(name)
56+
if flag == nil {
57+
err := fmt.Errorf("flag accessed but not defined: %s", name)
58+
return time.Time{}, err
59+
}
60+
61+
if flag.Value.Type() != "time" {
62+
err := fmt.Errorf("trying to get %s value of flag of type %s", "time", flag.Value.Type())
63+
return time.Time{}, err
64+
}
65+
66+
val, ok := flag.Value.(*TimeValue)
67+
if !ok {
68+
return time.Time{}, fmt.Errorf("value %s is not a time", flag.Value)
69+
}
70+
71+
return *val.Time, nil
72+
}
73+
74+
// TimeVar defines a time.Time flag with specified name, default value, and usage string.
75+
// The argument p points to a time.Time variable in which to store the value of the flag.
76+
func (f *FlagSet) TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
77+
f.VarP(newTimeValue(value, p, formats), name, "", usage)
78+
}
79+
80+
// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
81+
func (f *FlagSet) TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
82+
f.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
83+
}
84+
85+
// TimeVar defines a time.Time flag with specified name, default value, and usage string.
86+
// The argument p points to a time.Time variable in which to store the value of the flag.
87+
func TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
88+
CommandLine.VarP(newTimeValue(value, p, formats), name, "", usage)
89+
}
90+
91+
// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
92+
func TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
93+
CommandLine.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
94+
}
95+
96+
// Time defines a time.Time flag with specified name, default value, and usage string.
97+
// The return value is the address of a time.Time variable that stores the value of the flag.
98+
func (f *FlagSet) Time(name string, value time.Time, formats []string, usage string) *time.Time {
99+
p := new(time.Time)
100+
f.TimeVarP(p, name, "", value, formats, usage)
101+
return p
102+
}
103+
104+
// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
105+
func (f *FlagSet) TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
106+
p := new(time.Time)
107+
f.TimeVarP(p, name, shorthand, value, formats, usage)
108+
return p
109+
}
110+
111+
// Time defines a time.Time flag with specified name, default value, and usage string.
112+
// The return value is the address of a time.Time variable that stores the value of the flag.
113+
func Time(name string, value time.Time, formats []string, usage string) *time.Time {
114+
return CommandLine.TimeP(name, "", value, formats, usage)
115+
}
116+
117+
// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
118+
func TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
119+
return CommandLine.TimeP(name, shorthand, value, formats, usage)
120+
}

time_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package pflag
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
"time"
8+
)
9+
10+
func setUpTime(t *time.Time, formats []string) *FlagSet {
11+
f := NewFlagSet("test", ContinueOnError)
12+
f.TimeVar(t, "time", time.Time{}, formats, "Time")
13+
return f
14+
}
15+
16+
func TestTime(t *testing.T) {
17+
testCases := []struct {
18+
input string
19+
success bool
20+
expected string
21+
}{
22+
{"2022-01-01T01:01:01+00:00", true, "2022-01-01T01:01:01Z"},
23+
{" 2022-01-01T01:01:01+00:00", true, "2022-01-01T01:01:01Z"},
24+
{"2022-01-01T01:01:01+00:00 ", true, "2022-01-01T01:01:01Z"},
25+
{"2022-01-01T01:01:01+02:00", true, "2022-01-01T01:01:01+02:00"},
26+
{"2022-01-01T01:01:01.01+02:00", true, "2022-01-01T01:01:01.01+02:00"},
27+
{"Sat, 01 Jan 2022 01:01:01 +0000", true, "2022-01-01T01:01:01Z"},
28+
{"Sat, 01 Jan 2022 01:01:01 +0200", true, "2022-01-01T01:01:01+02:00"},
29+
{"Sat, 01 Jan 2022 01:01:01 +0000", true, "2022-01-01T01:01:01Z"},
30+
{"", false, ""},
31+
{"not a date", false, ""},
32+
{"2022-01-01 01:01:01", false, ""},
33+
{"2022-01-01T01:01:01", false, ""},
34+
{"01 Jan 2022 01:01:01 +0000", false, ""},
35+
{"Sat, 01 Jan 2022 01:01:01", false, ""},
36+
}
37+
38+
devnull, _ := os.Open(os.DevNull)
39+
os.Stderr = devnull
40+
for i := range testCases {
41+
var timeVar time.Time
42+
formats := []string{time.RFC3339Nano, time.RFC1123Z}
43+
f := setUpTime(&timeVar, formats)
44+
45+
tc := &testCases[i]
46+
47+
arg := fmt.Sprintf("--time=%s", tc.input)
48+
err := f.Parse([]string{arg})
49+
if err != nil && tc.success == true {
50+
t.Errorf("expected success, got %q", err)
51+
continue
52+
} else if err == nil && tc.success == false {
53+
t.Errorf("expected failure")
54+
continue
55+
} else if tc.success {
56+
timeResult, err := f.GetTime("time")
57+
if err != nil {
58+
t.Errorf("Got error trying to fetch the Time flag: %v", err)
59+
}
60+
if timeResult.Format(time.RFC3339Nano) != tc.expected {
61+
t.Errorf("expected %q, got %q", tc.expected, timeVar.Format(time.RFC3339Nano))
62+
}
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)