diff options
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/time/init.go | 23 | ||||
-rw-r--r-- | tpl/time/time.go | 64 | ||||
-rw-r--r-- | tpl/time/time_test.go | 38 |
3 files changed, 117 insertions, 8 deletions
diff --git a/tpl/time/init.go b/tpl/time/init.go index 3112999e4..7abb36637 100644 --- a/tpl/time/init.go +++ b/tpl/time/init.go @@ -34,15 +34,26 @@ func init() { // // If args are passed, call AsTime(). - if len(args) == 0 { + switch len(args) { + case 0: return ctx - } + case 1: + t, err := ctx.AsTime(args[0]) + if err != nil { + return err + } + return t + case 2: + t, err := ctx.AsTime(args[0], args[1]) + if err != nil { + return err + } + return t - t, err := ctx.AsTime(args[0]) - if err != nil { - return err + // 3 or more arguments. Currently not supported. + default: + return "Invalid arguments supplied to `time`. Refer to time documentation: https://gohugo.io/functions/time/" } - return t }, } diff --git a/tpl/time/time.go b/tpl/time/time.go index 598124648..c3a01003a 100644 --- a/tpl/time/time.go +++ b/tpl/time/time.go @@ -31,13 +31,73 @@ type Namespace struct{} // AsTime converts the textual representation of the datetime string into // a time.Time interface. -func (ns *Namespace) AsTime(v interface{}) (interface{}, error) { +func (ns *Namespace) AsTime(v interface{}, args ...interface{}) (interface{}, error) { t, err := cast.ToTimeE(v) if err != nil { return nil, err } - return t, nil + if len(args) == 0 { + return t, nil + } + + // Otherwise, if a location is specified, attempt to parse the time using the location specified. + // Note: In this case, we require the input variable to be a string for proper parsing. + // Note: We can't convert an existing parsed time by using the `Time.In()` as this CONVERTS/MODIFIES + // the resulting time. + + switch givenType := v.(type) { + case string: + // Good, we only support strings + break + + default: + return nil, fmt.Errorf("Creating a time instance with location requires a value of type String. Given type: %s", givenType) + } + + location, err := _time.LoadLocation(args[0].(string)) + if err != nil { + return nil, err + } + + // Note: Cast currently doesn't support time with non-default locations. For now, just inlining this. + // Reference: https://github.com/spf13/cast/pull/80 + + fmts := []string{ + _time.RFC3339, + "2006-01-02T15:04:05", // iso8601 without timezone + _time.RFC1123Z, + _time.RFC1123, + _time.RFC822Z, + _time.RFC822, + _time.RFC850, + _time.ANSIC, + _time.UnixDate, + _time.RubyDate, + "2006-01-02 15:04:05.999999999 -0700 MST", // Time.String() + "2006-01-02", + "02 Jan 2006", + "2006-01-02T15:04:05-0700", // RFC3339 without timezone hh:mm colon + "2006-01-02 15:04:05 -07:00", + "2006-01-02 15:04:05 -0700", + "2006-01-02 15:04:05Z07:00", // RFC3339 without T + "2006-01-02 15:04:05Z0700", // RFC3339 without T or timezone hh:mm colon + "2006-01-02 15:04:05", + _time.Kitchen, + _time.Stamp, + _time.StampMilli, + _time.StampMicro, + _time.StampNano, + } + + for _, dateType := range fmts { + t, err := _time.ParseInLocation(dateType, v.(string), location) + if err == nil { + return t, nil + } + } + + return nil, fmt.Errorf("Unable to ParseInLocation using date \"%s\" with timezone \"%s\"", v, location) } // Format converts the textual representation of the datetime string into diff --git a/tpl/time/time_test.go b/tpl/time/time_test.go index 01cf4e03b..d9e112878 100644 --- a/tpl/time/time_test.go +++ b/tpl/time/time_test.go @@ -18,6 +18,44 @@ import ( "time" ) +func TestTimeLocation(t *testing.T) { + t.Parallel() + + ns := New() + + for i, test := range []struct { + value string + location string + expect interface{} + }{ + {"2020-10-20", "", "2020-10-20 00:00:00 +0000 UTC"}, + {"2020-10-20", "America/New_York", "2020-10-20 00:00:00 -0400 EDT"}, + {"2020-01-20", "America/New_York", "2020-01-20 00:00:00 -0500 EST"}, + {"2020-10-20 20:33:59", "", "2020-10-20 20:33:59 +0000 UTC"}, + {"2020-10-20 20:33:59", "America/New_York", "2020-10-20 20:33:59 -0400 EDT"}, + // The following have an explicit offset specified. In this case, it overrides timezone + {"2020-09-23T20:33:44-0700", "", "2020-09-23 20:33:44 -0700 -0700"}, + {"2020-09-23T20:33:44-0700", "America/New_York", "2020-09-23 20:33:44 -0700 -0700"}, + {"2020-01-20", "invalid-timezone", false}, // unknown time zone invalid-timezone + {"invalid-value", "", false}, + } { + result, err := ns.AsTime(test.value, test.location) + if b, ok := test.expect.(bool); ok && !b { + if err == nil { + t.Errorf("[%d] AsTime didn't return an expected error, got %v", i, result) + } + } else { + if err != nil { + t.Errorf("[%d] AsTime failed: %s", i, err) + continue + } + if result.(time.Time).String() != test.expect { + t.Errorf("[%d] AsTime got %v but expected %v", i, result, test.expect) + } + } + } +} + func TestFormat(t *testing.T) { t.Parallel() |