Localize time.Format

Fixes #8797
This commit is contained in:
Bjørn Erik Pedersen 2021-07-26 18:28:57 +02:00
parent f9afba9335
commit a57dda854b
11 changed files with 395 additions and 20 deletions

127
common/htime/time.go Normal file
View file

@ -0,0 +1,127 @@
// Copyright 2021 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package htime
import (
"strings"
"time"
"github.com/go-playground/locales"
)
var (
longDayNames = []string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
shortDayNames = []string{
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
}
shortMonthNames = []string{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
}
longMonthNames = []string{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
}
)
func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
if ltr == nil {
panic("must provide a locales.Translator")
}
return TimeFormatter{
ltr: ltr,
}
}
// TimeFormatter is locale aware.
type TimeFormatter struct {
ltr locales.Translator
}
func (f TimeFormatter) Format(t time.Time, layout string) string {
if layout == "" {
return ""
}
if layout[0] == ':' {
// It may be one of Hugo's custom layouts.
switch strings.ToLower(layout[1:]) {
case "date_full":
return f.ltr.FmtDateFull(t)
case "date_long":
return f.ltr.FmtDateLong(t)
case "date_medium":
return f.ltr.FmtDateMedium(t)
case "date_short":
return f.ltr.FmtDateShort(t)
case "time_full":
return f.ltr.FmtTimeFull(t)
case "time_long":
return f.ltr.FmtTimeLong(t)
case "time_medium":
return f.ltr.FmtTimeMedium(t)
case "time_short":
return f.ltr.FmtTimeShort(t)
}
}
s := t.Format(layout)
monthIdx := t.Month() - 1 // Month() starts at 1.
dayIdx := t.Weekday()
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthAbbreviated(t.Month()))
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdayAbbreviated(t.Weekday()))
return s
}

111
common/htime/time_test.go Normal file
View file

@ -0,0 +1,111 @@
// Copyright 2021 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package htime
import (
"testing"
"time"
translators "github.com/bep/gotranslators"
qt "github.com/frankban/quicktest"
)
func TestTimeFormatter(t *testing.T) {
c := qt.New(t)
june06, _ := time.Parse("2006-Jan-02", "2018-Jun-06")
june06 = june06.Add(7777 * time.Second)
c.Run("Norsk nynorsk", func(c *qt.C) {
f := NewTimeFormatter(translators.Get("nn"))
c.Assert(f.Format(june06, "Monday Jan 2 2006"), qt.Equals, "onsdag juni 6 2018")
c.Assert(f.Format(june06, "Mon January 2 2006"), qt.Equals, "on. juni 6 2018")
c.Assert(f.Format(june06, "Mon Mon"), qt.Equals, "on. on.")
})
c.Run("Custom layouts Norsk nynorsk", func(c *qt.C) {
f := NewTimeFormatter(translators.Get("nn"))
c.Assert(f.Format(june06, ":date_full"), qt.Equals, "onsdag 6. juni 2018")
c.Assert(f.Format(june06, ":date_long"), qt.Equals, "6. juni 2018")
c.Assert(f.Format(june06, ":date_medium"), qt.Equals, "6. juni 2018")
c.Assert(f.Format(june06, ":date_short"), qt.Equals, "06.06.2018")
c.Assert(f.Format(june06, ":time_full"), qt.Equals, "kl. 02:09:37 UTC")
c.Assert(f.Format(june06, ":time_long"), qt.Equals, "02:09:37 UTC")
c.Assert(f.Format(june06, ":time_medium"), qt.Equals, "02:09:37")
c.Assert(f.Format(june06, ":time_short"), qt.Equals, "02:09")
})
c.Run("Custom layouts English", func(c *qt.C) {
f := NewTimeFormatter(translators.Get("en"))
c.Assert(f.Format(june06, ":date_full"), qt.Equals, "Wednesday, June 6, 2018")
c.Assert(f.Format(june06, ":date_long"), qt.Equals, "June 6, 2018")
c.Assert(f.Format(june06, ":date_medium"), qt.Equals, "Jun 6, 2018")
c.Assert(f.Format(june06, ":date_short"), qt.Equals, "6/6/18")
c.Assert(f.Format(june06, ":time_full"), qt.Equals, "2:09:37 am UTC")
c.Assert(f.Format(june06, ":time_long"), qt.Equals, "2:09:37 am UTC")
c.Assert(f.Format(june06, ":time_medium"), qt.Equals, "2:09:37 am")
c.Assert(f.Format(june06, ":time_short"), qt.Equals, "2:09 am")
})
c.Run("English", func(c *qt.C) {
f := NewTimeFormatter(translators.Get("en"))
c.Assert(f.Format(june06, "Monday Jan 2 2006"), qt.Equals, "Wednesday Jun 6 2018")
c.Assert(f.Format(june06, "Mon January 2 2006"), qt.Equals, "Wed June 6 2018")
c.Assert(f.Format(june06, "Mon Mon"), qt.Equals, "Wed Wed")
})
}
func BenchmarkTimeFormatter(b *testing.B) {
june06, _ := time.Parse("2006-Jan-02", "2018-Jun-06")
b.Run("Native", func(b *testing.B) {
for i := 0; i < b.N; i++ {
got := june06.Format("Monday Jan 2 2006")
if got != "Wednesday Jun 6 2018" {
b.Fatalf("invalid format, got %q", got)
}
}
})
b.Run("Localized", func(b *testing.B) {
f := NewTimeFormatter(translators.Get("nn"))
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := f.Format(june06, "Monday Jan 2 2006")
if got != "onsdag juni 6 2018" {
b.Fatalf("invalid format, got %q", got)
}
}
})
b.Run("Localized Custom", func(b *testing.B) {
f := NewTimeFormatter(translators.Get("nn"))
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := f.Format(june06, ":date_medium")
if got != "6. juni 2018" {
b.Fatalf("invalid format, got %q", got)
}
}
})
}

View file

@ -1,6 +1,6 @@
--- ---
title: dateFormat title: time.Format
description: Converts the textual representation of the `datetime` into the specified format. description: Converts a date/time to a localized string.
godocref: https://golang.org/pkg/time/ godocref: https://golang.org/pkg/time/
date: 2017-02-01 date: 2017-02-01
publishdate: 2017-02-01 publishdate: 2017-02-01
@ -10,23 +10,47 @@ menu:
docs: docs:
parent: "functions" parent: "functions"
keywords: [dates,time,strings] keywords: [dates,time,strings]
signature: ["dateFormat LAYOUT INPUT"] signature: ["time.Format LAYOUT INPUT"]
workson: [] workson: []
hugoversion: hugoversion:
relatedfuncs: [Format,now,Unix,time] relatedfuncs: [Format,now,Unix,time]
deprecated: false deprecated: false
--- ---
`dateFormat` converts a timestamp string `INPUT` into the format specified by the `LAYOUT` string. `time.Format` (alias `dateFormat`) converts either a `time.Time` object (e.g. `.Date`) or a timestamp string `INPUT` into the format specified by the `LAYOUT` string.
``` ```go-html-template
{{ dateFormat "Monday, Jan 2, 2006" "2015-01-21" }} → "Wednesday, Jan 21, 2015" {{ time.Format "Monday, Jan 2, 2006" "2015-01-21" }} → "Wednesday, Jan 21, 2015"
``` ```
{{% warning %}} Note that since Hugo 0.87.0, `time.Format` will return a localized string for the currrent language. {{< new-in "0.87.0" >}}
As of v0.19 of Hugo, the `dateFormat` function is *not* supported as part of Hugo's [multilingual feature](/content-management/multilingual/).
{{% /warning %}}
See [Gos Layout String](/functions/format/#gos-layout-string) to learn about how the `LAYOUT` string has to be formatted. There are also some useful examples. The `LAYOUT` string can be either:
* [Gos Layout String](/functions/format/#gos-layout-string) to learn about how the `LAYOUT` string has to be formatted. There are also some useful examples.
* A custom Hugo layout identifier (see full list below)
See the [`time` function](/functions/time/) to convert a timestamp string to a Go `time.Time` type value. See the [`time` function](/functions/time/) to convert a timestamp string to a Go `time.Time` type value.
## Date/time formatting layouts
{{< new-in "0.87.0" >}}
Go's date layout strings can be hard to reason about, especially with multiple languages. Since Hugo 0.87.0 you can alternatively use some predefined layout idenfifiers that will output localized dates or times:
```go-html-template
{{ .Date | time.Format ":date_long" }}
```
The full list of custom layouts with examples for English:
* `:date_full` => `Wednesday, June 6, 2018`
* `:date_long` => `June 6, 2018`
* `:date_medium` => `Jun 6, 2018`
* `:date_short` => `6/6/18`
* `:time_full` => `2:09:37 am UTC`
* `:time_long` => `2:09:37 am UTC`
* `:time_medium` => `2:09:37 am`
* `:time_short` => `2:09 am`

2
go.mod
View file

@ -13,6 +13,7 @@ require (
github.com/bep/gitmap v1.1.2 github.com/bep/gitmap v1.1.2
github.com/bep/godartsass v0.12.0 github.com/bep/godartsass v0.12.0
github.com/bep/golibsass v1.0.0 github.com/bep/golibsass v1.0.0
github.com/bep/gotranslators v0.0.0-20210726170149-50377fc92c80
github.com/bep/gowebp v0.1.0 github.com/bep/gowebp v0.1.0
github.com/bep/tmc v0.5.1 github.com/bep/tmc v0.5.1
github.com/cli/safeexec v1.0.0 github.com/cli/safeexec v1.0.0
@ -24,6 +25,7 @@ require (
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/getkin/kin-openapi v0.67.0 github.com/getkin/kin-openapi v0.67.0
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
github.com/go-playground/locales v0.13.0
github.com/gobuffalo/flect v0.2.3 github.com/gobuffalo/flect v0.2.3
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20210430103248-4c28c89f8013 github.com/gohugoio/go-i18n/v2 v2.1.3-0.20210430103248-4c28c89f8013

6
go.sum
View file

@ -134,10 +134,14 @@ github.com/bep/godartsass v0.12.0 h1:VvGLA4XpXUjKvp53SI05YFLhRFJ78G+Ybnlaz6Oul7E
github.com/bep/godartsass v0.12.0/go.mod h1:nXQlHHk4H1ghUk6n/JkYKG5RD43yJfcfp5aHRqT/pc4= github.com/bep/godartsass v0.12.0/go.mod h1:nXQlHHk4H1ghUk6n/JkYKG5RD43yJfcfp5aHRqT/pc4=
github.com/bep/golibsass v1.0.0 h1:gNguBMSDi5yZEZzVZP70YpuFQE3qogJIGUlrVILTmOw= github.com/bep/golibsass v1.0.0 h1:gNguBMSDi5yZEZzVZP70YpuFQE3qogJIGUlrVILTmOw=
github.com/bep/golibsass v1.0.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= github.com/bep/golibsass v1.0.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
github.com/bep/gotranslators v0.0.0-20210726170149-50377fc92c80 h1:FuOr7TE02FmHwf0HbOzfN0UyQfHoZd1R3PVuYduFU6U=
github.com/bep/gotranslators v0.0.0-20210726170149-50377fc92c80/go.mod h1:/tUOv4Jdczp4ZggwBAQriNN97HsQdG1Gm+yV0PsIGD8=
github.com/bep/gowebp v0.1.0 h1:4/iQpfnxHyXs3x/aTxMMdOpLEQQhFmF6G7EieWPTQyo= github.com/bep/gowebp v0.1.0 h1:4/iQpfnxHyXs3x/aTxMMdOpLEQQhFmF6G7EieWPTQyo=
github.com/bep/gowebp v0.1.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI= github.com/bep/gowebp v0.1.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI= github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0= github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0=
github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg=
github.com/bep/workers v1.0.0/go.mod h1:7kIESOB86HfR2379pwoMWNy8B50D7r99fRLUyPSNyCs=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -210,6 +214,8 @@ github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUe
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY= github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY=
github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=

56
hugolib/dates_test.go Normal file
View file

@ -0,0 +1,56 @@
// Copyright 2021 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugolib
import (
"testing"
)
func TestDateFormatMultilingual(t *testing.T) {
b := newTestSitesBuilder(t)
b.WithConfigFile("toml", `
baseURL = "https://example.org"
defaultContentLanguage = "en"
defaultContentLanguageInSubDir = true
[languages]
[languages.en]
weight=10
[languages.nn]
weight=20
`)
pageWithDate := `---
title: Page
date: 2021-07-18
---
`
b.WithContent(
"_index.en.md", pageWithDate,
"_index.nn.md", pageWithDate,
)
b.WithTemplatesAdded("index.html", `
Date: {{ .Date | time.Format ":date_long" }}
`)
b.Build(BuildCfg{})
b.AssertFileContent("public/en/index.html", `Date: July 18, 2021`)
b.AssertFileContent("public/nn/index.html", `Date: 18. juli 2021`)
}

View file

@ -18,6 +18,8 @@ import (
"strings" "strings"
"sync" "sync"
translators "github.com/bep/gotranslators"
"github.com/go-playground/locales"
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
) )
@ -68,6 +70,11 @@ type Language struct {
params map[string]interface{} params map[string]interface{}
paramsMu sync.Mutex paramsMu sync.Mutex
paramsSet bool paramsSet bool
// Used for date formatting etc. We don't want this exported to the
// templates.
// TODO(bep) do the same for some of the others.
translator locales.Translator
} }
func (l *Language) String() string { func (l *Language) String() string {
@ -86,8 +93,22 @@ func NewLanguage(lang string, cfg config.Provider) *Language {
localCfg := config.New() localCfg := config.New()
compositeConfig := config.NewCompositeConfig(cfg, localCfg) compositeConfig := config.NewCompositeConfig(cfg, localCfg)
translator := translators.Get(lang)
if translator == nil {
translator = translators.Get(cfg.GetString("defaultContentLanguage"))
if translator == nil {
translator = translators.Get("en")
}
}
l := &Language{Lang: lang, ContentDir: cfg.GetString("contentDir"), Cfg: cfg, LocalCfg: localCfg, Provider: compositeConfig, params: params} l := &Language{
Lang: lang,
ContentDir: cfg.GetString("contentDir"),
Cfg: cfg, LocalCfg: localCfg,
Provider: compositeConfig,
params: params,
translator: translator,
}
return l return l
} }
@ -222,3 +243,10 @@ func (l *Language) IsSet(key string) bool {
} }
return l.Cfg.IsSet(key) return l.Cfg.IsSet(key)
} }
// Internal access to unexported Language fields.
// This construct is to prevent them from leaking to the templates.
func GetTranslator(l *Language) locales.Translator {
return l.translator
}

View file

@ -15,6 +15,7 @@ package time
import ( import (
"github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/tpl/internal" "github.com/gohugoio/hugo/tpl/internal"
) )
@ -22,7 +23,10 @@ const name = "time"
func init() { func init() {
f := func(d *deps.Deps) *internal.TemplateFuncsNamespace { f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
ctx := New() if d.Language == nil {
panic("Language must be set")
}
ctx := New(langs.GetTranslator(d.Language))
ns := &internal.TemplateFuncsNamespace{ ns := &internal.TemplateFuncsNamespace{
Name: name, Name: name,

View file

@ -16,6 +16,9 @@ package time
import ( import (
"testing" "testing"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/htesting/hqt" "github.com/gohugoio/hugo/htesting/hqt"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
@ -29,7 +32,9 @@ func TestInit(t *testing.T) {
var ns *internal.TemplateFuncsNamespace var ns *internal.TemplateFuncsNamespace
for _, nsf := range internal.TemplateFuncsNamespaceRegistry { for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
ns = nsf(&deps.Deps{}) ns = nsf(&deps.Deps{
Language: langs.NewDefaultLanguage(config.New()),
})
if ns.Name == name { if ns.Name == name {
found = true found = true
break break

View file

@ -18,6 +18,10 @@ import (
"fmt" "fmt"
_time "time" _time "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/go-playground/locales"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@ -49,12 +53,16 @@ var timeFormats = []string{
} }
// New returns a new instance of the time-namespaced template functions. // New returns a new instance of the time-namespaced template functions.
func New() *Namespace { func New(translator locales.Translator) *Namespace {
return &Namespace{} return &Namespace{
timeFormatter: htime.NewTimeFormatter(translator),
}
} }
// Namespace provides template functions for the "time" namespace. // Namespace provides template functions for the "time" namespace.
type Namespace struct{} type Namespace struct {
timeFormatter htime.TimeFormatter
}
// 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.
@ -105,7 +113,7 @@ func (ns *Namespace) Format(layout string, v interface{}) (string, error) {
return "", err return "", err
} }
return t.Format(layout), nil return ns.timeFormatter.Format(t, layout), nil
} }
// Now returns the current local time. // Now returns the current local time.

View file

@ -16,12 +16,14 @@ package time
import ( import (
"testing" "testing"
"time" "time"
translators "github.com/bep/gotranslators"
) )
func TestTimeLocation(t *testing.T) { func TestTimeLocation(t *testing.T) {
t.Parallel() t.Parallel()
ns := New() ns := New(translators.Get("en"))
for i, test := range []struct { for i, test := range []struct {
value string value string
@ -59,7 +61,7 @@ func TestTimeLocation(t *testing.T) {
func TestFormat(t *testing.T) { func TestFormat(t *testing.T) {
t.Parallel() t.Parallel()
ns := New() ns := New(translators.Get("en"))
for i, test := range []struct { for i, test := range []struct {
layout string layout string
@ -76,6 +78,8 @@ func TestFormat(t *testing.T) {
{time.RFC1123, time.Date(2016, time.March, 3, 4, 5, 0, 0, time.UTC), "Thu, 03 Mar 2016 04:05:00 UTC"}, {time.RFC1123, time.Date(2016, time.March, 3, 4, 5, 0, 0, time.UTC), "Thu, 03 Mar 2016 04:05:00 UTC"},
{time.RFC3339, "Thu, 03 Mar 2016 04:05:00 UTC", "2016-03-03T04:05:00Z"}, {time.RFC3339, "Thu, 03 Mar 2016 04:05:00 UTC", "2016-03-03T04:05:00Z"},
{time.RFC1123, "2016-03-03T04:05:00Z", "Thu, 03 Mar 2016 04:05:00 UTC"}, {time.RFC1123, "2016-03-03T04:05:00Z", "Thu, 03 Mar 2016 04:05:00 UTC"},
// Custom layouts, as introduced in Hugo 0.87.
{":date_medium", "2015-01-21", "Jan 21, 2015"},
} { } {
result, err := ns.Format(test.layout, test.value) result, err := ns.Format(test.layout, test.value)
if b, ok := test.expect.(bool); ok && !b { if b, ok := test.expect.(bool); ok && !b {
@ -97,7 +101,7 @@ func TestFormat(t *testing.T) {
func TestDuration(t *testing.T) { func TestDuration(t *testing.T) {
t.Parallel() t.Parallel()
ns := New() ns := New(translators.Get("en"))
for i, test := range []struct { for i, test := range []struct {
unit interface{} unit interface{}