From 1335a7022a9ebf6cc5f9568d0d5c31d384c1955e Mon Sep 17 00:00:00 2001 From: Balazs Nagy Date: Sat, 9 Jul 2022 13:43:14 +0200 Subject: [PATCH] accept timezone for timestamps --- docs/v2/manual.md | 13 +++++++++++++ flag-spec.yaml | 1 + flag_test.go | 12 ++++++++++++ flag_timestamp.go | 18 +++++++++++++++++- godoc-current.txt | 8 ++++++++ zz_generated.flags.go | 2 ++ 6 files changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/v2/manual.md b/docs/v2/manual.md index ebff418cc6..475e047fdd 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -1413,6 +1413,19 @@ In this example the flag could be used like this: $ myapp --meeting 2019-08-12T15:04:05 ``` +When the layout doesn't contain timezones, timestamp will render with UTC. To +change behavior, a default timezone can be provided with flag definition: + +```go +app := &cli.App{ + Flags: []cli.Flag{ + &cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05", Timezone: time.Local}, + }, +} +``` + +(time.Local contains the system's local time zone.) + Side note: quotes may be necessary around the date depending on your layout (if you have spaces for instance) diff --git a/flag-spec.yaml b/flag-spec.yaml index d85fa30bd1..45f054d6e5 100644 --- a/flag-spec.yaml +++ b/flag-spec.yaml @@ -43,6 +43,7 @@ flag_types: value_pointer: true struct_fields: - { name: Layout, type: string } + - { name: Timezone, type: "*time.Location" } # TODO: enable UintSlice # UintSlice: {} diff --git a/flag_test.go b/flag_test.go index 0bb893afb6..e46b1eff45 100644 --- a/flag_test.go +++ b/flag_test.go @@ -2364,6 +2364,18 @@ func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) { expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\"")) } +func TestTimestampFlagApply_Timezoned(t *testing.T) { + pdt := time.FixedZone("PDT", -7*60*60) + expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") + fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.ANSIC, Timezone: pdt} + set := flag.NewFlagSet("test", 0) + _ = fl.Apply(set) + + err := set.Parse([]string{"--time", "Mon Jan 2 08:04:05 2006"}) + expect(t, err, nil) + expect(t, *fl.Value.timestamp, expectedResult.In(pdt)) +} + func TestTimestampFlagValueFromContext(t *testing.T) { set := flag.NewFlagSet("test", 0) now := time.Now() diff --git a/flag_timestamp.go b/flag_timestamp.go index 052247795c..80e1f470be 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -11,6 +11,7 @@ type Timestamp struct { timestamp *time.Time hasBeenSet bool layout string + location *time.Location } // Timestamp constructor @@ -31,9 +32,22 @@ func (t *Timestamp) SetLayout(layout string) { t.layout = layout } +// Set perceived timezone of the to-be parsed time string +func (t *Timestamp) SetLocation(loc *time.Location) { + t.location = loc +} + // Parses the string value to timestamp func (t *Timestamp) Set(value string) error { - timestamp, err := time.Parse(t.layout, value) + var timestamp time.Time + var err error + + if t.location != nil { + timestamp, err = time.ParseInLocation(t.layout, value, t.location) + } else { + timestamp, err = time.Parse(t.layout, value) + } + if err != nil { return err } @@ -104,9 +118,11 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { f.Value = &Timestamp{} } f.Value.SetLayout(f.Layout) + f.Value.SetLocation(f.Timezone) if f.Destination != nil { f.Destination.SetLayout(f.Layout) + f.Destination.SetLocation(f.Timezone) } if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { diff --git a/godoc-current.txt b/godoc-current.txt index a3a7faca17..1ba48cc659 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -269,6 +269,9 @@ type App struct { Version string // Description of the program Description string + // DefaultCommand is the (optional) name of a command + // to run if no command names are passed as CLI arguments. + DefaultCommand string // List of commands to execute Commands []*Command // List of flags to parse @@ -1754,6 +1757,9 @@ func (t *Timestamp) Set(value string) error func (t *Timestamp) SetLayout(layout string) Set the timestamp string layout for future parsing +func (t *Timestamp) SetLocation(loc *time.Location) + Set perceived timezone of the to-be parsed time string + func (t *Timestamp) SetTimestamp(value time.Time) Set the timestamp value directly @@ -1782,6 +1788,8 @@ type TimestampFlag struct { EnvVars []string Layout string + + Timezone *time.Location } TimestampFlag is a flag with type *Timestamp diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 3cae978c0c..b89566f900 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -280,6 +280,8 @@ type TimestampFlag struct { EnvVars []string Layout string + + Timezone *time.Location } // String returns a readable representation of this value (for usage defaults)