tpl: Refactor time.AsTime location implementation
authorCameron Moore <moorereason@gmail.com>
Wed, 21 Oct 2020 01:07:11 +0000 (20:07 -0500)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 21 Oct 2020 07:49:25 +0000 (09:49 +0200)
docs/content/en/functions/time.md
tpl/time/time.go

index 57d5f65f8810ab593127d79ba5bef0652220703c..c4f74215bd2abeb70652a6ca1ed2be1c05dea956 100644 (file)
@@ -13,13 +13,13 @@ menu:
 keywords: [dates,time,location]
 signature: ["time INPUT [LOCATION]"]
 workson: []
-hugoversion:
+hugoversion: "v0.77.0"
 relatedfuncs: []
 deprecated: false
 aliases: []
 ---
 
-`time` converts a timestamp string with an optional timezone into a [`time.Time`](https://godoc.org/time#Time) structure so you can access its fields:
+`time` converts a timestamp string with an optional default location into a [`time.Time`](https://godoc.org/time#Time) structure so you can access its fields:
 
 ```
 {{ time "2016-05-28" }} → "2016-05-28T00:00:00Z"
@@ -27,9 +27,11 @@ aliases: []
 {{ mul 1000 (time "2016-05-28T10:30:00.00+10:00").Unix }} → 1464395400000, or Unix time in milliseconds
 ```
 
-## Using Timezone
+## Using Locations
 
-The optional 2nd parameter [LOCATION] argument is a string that references a timezone that is associated with the specified time value. If the time value has an explicit timezone or offset specified, it will take precedence over an explicit [LOCATION].
+The optional `LOCATION` parameter is a string that sets a default location that is associated with the specified time value. If the time value has an explicit timezone or offset specified, it will take precedence over the `LOCATION` parameter.
+
+The list of valid locations may be system dependent, but should include `UTC`, `Local`, or any location in the [IANA Time Zone database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
 
 ```
 {{ time "2020-10-20" }} → 2020-10-20 00:00:00 +0000 UTC
@@ -37,8 +39,6 @@ The optional 2nd parameter [LOCATION] argument is a string that references a tim
 {{ time "2020-01-20" "America/Los_Angeles" }} → 2020-01-20 00:00:00 -0800 PST
 ```
 
-> **Note**: Timezone support via the [LOCATION] parameter is included with Hugo `0.77`.
-
 ## Example: Using `time` to get Month Index
 
 The following example takes a UNIX timestamp---set as `utimestamp: "1489276800"` in a content's front matter---converts the timestamp (string) to an integer using the [`int` function][int], and then uses [`printf`][] to convert the `Month` property of `time` into an index.
index c3a01003a07760159e997c234ae69e8e005fdeb6..8ed4606d6e61a017737156a93219ebaa3c0f9495 100644 (file)
@@ -21,6 +21,33 @@ import (
        "github.com/spf13/cast"
 )
 
+var timeFormats = []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,
+}
+
 // New returns a new instance of the time-namespaced template functions.
 func New() *Namespace {
        return &Namespace{}
@@ -32,30 +59,26 @@ type Namespace struct{}
 // AsTime converts the textual representation of the datetime string into
 // a time.Time interface.
 func (ns *Namespace) AsTime(v interface{}, args ...interface{}) (interface{}, error) {
-       t, err := cast.ToTimeE(v)
-       if err != nil {
-               return nil, err
-       }
-
        if len(args) == 0 {
+               t, err := cast.ToTimeE(v)
+               if err != nil {
+                       return nil, err
+               }
+
                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
+       timeStr, err := cast.ToStringE(v)
+       if err != nil {
+               return nil, err
+       }
 
-       default:
-               return nil, fmt.Errorf("Creating a time instance with location requires a value of type String. Given type: %s", givenType)
+       locStr, err := cast.ToStringE(args[0])
+       if err != nil {
+               return nil, err
        }
 
-       location, err := _time.LoadLocation(args[0].(string))
+       loc, err := _time.LoadLocation(locStr)
        if err != nil {
                return nil, err
        }
@@ -63,41 +86,14 @@ func (ns *Namespace) AsTime(v interface{}, args ...interface{}) (interface{}, er
        // 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 {
+       for _, dateType := range timeFormats {
+               t, err2 := _time.ParseInLocation(dateType, timeStr, loc)
+               if err2 == nil {
                        return t, nil
                }
        }
 
-       return nil, fmt.Errorf("Unable to ParseInLocation using date \"%s\" with timezone \"%s\"", v, location)
+       return nil, fmt.Errorf("Unable to ParseInLocation using date %q with timezone %q", v, loc)
 }
 
 // Format converts the textual representation of the datetime string into