forked from ogier/pflag
-
Notifications
You must be signed in to change notification settings - Fork 364
Add support for time.Time flags #348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+180
−0
Merged
Changes from 3 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
1992c5a
Add support for time.Time flags
max-frank c5ce22e
Use time.Time for expectations in time flag tests
max-frank d15848d
Remove unnecessary time test stderr dev null redirect
max-frank 7cc25e3
Don't export `TimeValue` (yet)
tomasaschan e3be2eb
Reduce duplication by forwarding to sibling functions
tomasaschan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| 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, | ||
| } | ||
| } | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Set time.Time value from string based on accepted formats. | ||
| func (d *TimeValue) Set(s string) error { | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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 { | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return "time" | ||
| } | ||
|
|
||
| func (d *TimeValue) String() string { return d.Time.Format(time.RFC3339Nano) } | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // 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) | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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.VarP(newTimeValue(value, p, formats), name, "", usage) | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // 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.VarP(newTimeValue(value, p, formats), name, "", usage) | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // 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 { | ||
| p := new(time.Time) | ||
| f.TimeVarP(p, name, "", value, formats, usage) | ||
| return p | ||
tomasaschan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // 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) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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)) | ||
| } | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.