mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-28 23:52:04 -05:00
tpl: Update Hugo time to support optional [LOCATION] parameter
This commit is contained in:
parent
b886fa46bb
commit
26eeb29147
4 changed files with 132 additions and 11 deletions
|
@ -10,8 +10,8 @@ categories: [functions]
|
||||||
menu:
|
menu:
|
||||||
docs:
|
docs:
|
||||||
parent: "functions"
|
parent: "functions"
|
||||||
keywords: [dates,time]
|
keywords: [dates,time,location]
|
||||||
signature: ["time INPUT"]
|
signature: ["time INPUT [LOCATION]"]
|
||||||
workson: []
|
workson: []
|
||||||
hugoversion:
|
hugoversion:
|
||||||
relatedfuncs: []
|
relatedfuncs: []
|
||||||
|
@ -19,7 +19,7 @@ deprecated: false
|
||||||
aliases: []
|
aliases: []
|
||||||
---
|
---
|
||||||
|
|
||||||
`time` converts a timestamp string into a [`time.Time`](https://godoc.org/time#Time) structure so you can access its fields:
|
`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 "2016-05-28" }} → "2016-05-28T00:00:00Z"
|
{{ time "2016-05-28" }} → "2016-05-28T00:00:00Z"
|
||||||
|
@ -27,6 +27,18 @@ aliases: []
|
||||||
{{ mul 1000 (time "2016-05-28T10:30:00.00+10:00").Unix }} → 1464395400000, or Unix time in milliseconds
|
{{ mul 1000 (time "2016-05-28T10:30:00.00+10:00").Unix }} → 1464395400000, or Unix time in milliseconds
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Using Timezone
|
||||||
|
|
||||||
|
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].
|
||||||
|
|
||||||
|
```
|
||||||
|
{{ time "2020-10-20" }} → 2020-10-20 00:00:00 +0000 UTC
|
||||||
|
{{ time "2020-10-20" "America/Los_Angeles" }} → 2020-10-20 00:00:00 -0700 PDT
|
||||||
|
{{ 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
|
## 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.
|
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.
|
||||||
|
|
|
@ -34,15 +34,26 @@ func init() {
|
||||||
//
|
//
|
||||||
// If args are passed, call AsTime().
|
// If args are passed, call AsTime().
|
||||||
|
|
||||||
if len(args) == 0 {
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
return ctx
|
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])
|
// 3 or more arguments. Currently not supported.
|
||||||
if err != nil {
|
default:
|
||||||
return err
|
return "Invalid arguments supplied to `time`. Refer to time documentation: https://gohugo.io/functions/time/"
|
||||||
}
|
}
|
||||||
return t
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,73 @@ type Namespace struct{}
|
||||||
|
|
||||||
// AsTime converts the textual representation of the datetime string into
|
// AsTime converts the textual representation of the datetime string into
|
||||||
// a time.Time interface.
|
// 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)
|
t, err := cast.ToTimeE(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Format converts the textual representation of the datetime string into
|
||||||
|
|
|
@ -18,6 +18,44 @@ import (
|
||||||
"time"
|
"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) {
|
func TestFormat(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue