aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Aschan <[email protected]>2025-07-16 23:42:25 +0200
committerGitHub <[email protected]>2025-07-16 23:42:25 +0200
commitf9cbdd9ca94287ab4ef0848e67ecd77cf1361d48 (patch)
treef1ac1605fcce5193d2d9d16969e90db2fc59ede7
parent1c62fb2813da5f1d1b893a49180a41b3f6be3262 (diff)
parente3be2ebcffcc36be35e23d418d3e0ba86239826a (diff)
Merge pull request #348 from max-frank/add-time-flag-supportv1.0.7
Add support for time.Time flags
-rw-r--r--time.go118
-rw-r--r--time_test.go62
2 files changed, 180 insertions, 0 deletions
diff --git a/time.go b/time.go
new file mode 100644
index 0000000..dc02480
--- /dev/null
+++ b/time.go
@@ -0,0 +1,118 @@
+package pflag
+
+import (
+ "fmt"
+ "strings"
+ "time"
+)
+
+// TimeValue adapts time.Time for use as a flag.
+type timeValue struct {
+ *time.Time
+ formats []string
+}
+
+func newTimeValue(val time.Time, p *time.Time, formats []string) *timeValue {
+ *p = val
+ return &timeValue{
+ Time: p,
+ formats: formats,
+ }
+}
+
+// Set time.Time value from string based on accepted formats.
+func (d *timeValue) Set(s string) error {
+ s = strings.TrimSpace(s)
+ for _, f := range d.formats {
+ v, err := time.Parse(f, s)
+ if err != nil {
+ continue
+ }
+ *d.Time = v
+ return nil
+ }
+
+ formatsString := ""
+ for i, f := range d.formats {
+ if i > 0 {
+ formatsString += ", "
+ }
+ formatsString += fmt.Sprintf("`%s`", f)
+ }
+
+ return fmt.Errorf("invalid time format `%s` must be one of: %s", s, formatsString)
+}
+
+// Type name for time.Time flags.
+func (d *timeValue) Type() string {
+ return "time"
+}
+
+func (d *timeValue) String() string { return d.Time.Format(time.RFC3339Nano) }
+
+// GetTime return the time value of a flag with the given name
+func (f *FlagSet) GetTime(name string) (time.Time, error) {
+ flag := f.Lookup(name)
+ if flag == nil {
+ err := fmt.Errorf("flag accessed but not defined: %s", name)
+ return time.Time{}, err
+ }
+
+ if flag.Value.Type() != "time" {
+ err := fmt.Errorf("trying to get %s value of flag of type %s", "time", flag.Value.Type())
+ return time.Time{}, err
+ }
+
+ val, ok := flag.Value.(*timeValue)
+ if !ok {
+ return time.Time{}, fmt.Errorf("value %s is not a time", flag.Value)
+ }
+
+ return *val.Time, nil
+}
+
+// TimeVar 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 (f *FlagSet) TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
+ f.TimeVarP(p, name, "", value, formats, usage)
+}
+
+// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
+ f.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
+}
+
+// TimeVar 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 TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
+ CommandLine.TimeVarP(p, name, "", value, formats, usage)
+}
+
+// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
+func TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
+ CommandLine.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
+}
+
+// Time 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 (f *FlagSet) Time(name string, value time.Time, formats []string, usage string) *time.Time {
+ return f.TimeP(name, "", value, formats, usage)
+}
+
+// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
+ p := new(time.Time)
+ f.TimeVarP(p, name, shorthand, value, formats, usage)
+ return p
+}
+
+// Time 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 Time(name string, value time.Time, formats []string, usage string) *time.Time {
+ return CommandLine.TimeP(name, "", value, formats, usage)
+}
+
+// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
+func TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
+ return CommandLine.TimeP(name, shorthand, value, formats, usage)
+}
diff --git a/time_test.go b/time_test.go
new file mode 100644
index 0000000..46a5ada
--- /dev/null
+++ b/time_test.go
@@ -0,0 +1,62 @@
+package pflag
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+func setUpTimeVar(t *time.Time, formats []string) *FlagSet {
+ f := NewFlagSet("test", ContinueOnError)
+ f.TimeVar(t, "time", time.Time{}, formats, "Time")
+ return f
+}
+
+func TestTime(t *testing.T) {
+ testCases := []struct {
+ input string
+ success bool
+ expected time.Time
+ }{
+ {"2022-01-01T01:01:01+00:00", true, time.Date(2022, 1, 1, 1, 1, 1, 0, time.UTC)},
+ {" 2022-01-01T01:01:01+00:00", true, time.Date(2022, 1, 1, 1, 1, 1, 0, time.UTC)},
+ {"2022-01-01T01:01:01+00:00 ", true, time.Date(2022, 1, 1, 1, 1, 1, 0, time.UTC)},
+ {"2022-01-01T01:01:01+02:00", true, time.Date(2022, 1, 1, 1, 1, 1, 0, time.FixedZone("UTC+2", 2*60*60))},
+ {"2022-01-01T01:01:01.01+02:00", true, time.Date(2022, 1, 1, 1, 1, 1, 10000000, time.FixedZone("UTC+2", 2*60*60))},
+ {"Sat, 01 Jan 2022 01:01:01 +0000", true, time.Date(2022, 1, 1, 1, 1, 1, 0, time.UTC)},
+ {"Sat, 01 Jan 2022 01:01:01 +0200", true, time.Date(2022, 1, 1, 1, 1, 1, 0, time.FixedZone("UTC+2", 2*60*60))},
+ {"Sat, 01 Jan 2022 01:01:01 +0000", true, time.Date(2022, 1, 1, 1, 1, 1, 0, time.UTC)},
+ {"", false, time.Time{}},
+ {"not a date", false, time.Time{}},
+ {"2022-01-01 01:01:01", false, time.Time{}},
+ {"2022-01-01T01:01:01", false, time.Time{}},
+ {"01 Jan 2022 01:01:01 +0000", false, time.Time{}},
+ {"Sat, 01 Jan 2022 01:01:01", false, time.Time{}},
+ }
+
+ for i := range testCases {
+ var timeVar time.Time
+ formats := []string{time.RFC3339Nano, time.RFC1123Z}
+ f := setUpTimeVar(&timeVar, formats)
+
+ tc := &testCases[i]
+
+ arg := fmt.Sprintf("--time=%s", tc.input)
+ err := f.Parse([]string{arg})
+ if err != nil && tc.success == true {
+ t.Errorf("expected success, got %q", err)
+ continue
+ } else if err == nil && tc.success == false {
+ t.Errorf("expected failure")
+ continue
+ } else if tc.success {
+ timeResult, err := f.GetTime("time")
+ if err != nil {
+ t.Errorf("Got error trying to fetch the Time flag: %v", err)
+ }
+ if !timeResult.Equal(tc.expected) {
+ t.Errorf("expected %q, got %q", tc.expected.Format(time.RFC3339Nano), timeVar.Format(time.RFC3339Nano))
+ }
+ }
+ }
+}