Hugo time.Format 函数完全指南:日期时间格式化、时区与本地化实战
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
Hugo 模板中的time.Format函数用于把time.Time值或可解析的日期/时间字符串,按照自定义布局或本地化标记格式化为字符串输出。本指南将围绕该函数在 Hugo 项目中的实际应用场景,完整讲解其基本用法、时区优先级、Go 参考时间布局、本地化令牌与源码级实现原理,帮助你在站点模板中精确控制日期时间的显示效果。
函数签名与别名
根据 docs/content/en/functions/time/Format.md 中的 front matter 定义:
- 签名:
time.Format LAYOUT INPUT - 返回类型:
string - 别名:
dateFormat
别名注册位于 tpl/time/init.go,模板中可直接使用dateFormat:
{{ dateFormat "Monday, Jan 2, 2006" "2015-01-21" }} → Wednesday, Jan 21, 2015该映射由ns.AddMethodMapping(ctx.Format, []string{"dateFormat"}, ...)注册,同时保留了time.Format的正式名称。
基本用法
配合 time.Time 值使用
先通过time.AsTime把字符串转换为time.Time,再交给time.Format:
{{ $t := time.AsTime "2023-10-15T13:18:50-07:00" }} {{ time.Format "2 Jan 2006" $t }} → 15 Oct 2023配合可解析的字符串使用
time.Format内部会先尝试把输入值转换为time.Time(见下文源码解析),因此可以直接传入字符串:
{{ $t := "15 Oct 2023" }} {{ time.Format "January 2, 2006" $t }} → October 15, 2023管道写法
{{ .Date | time.Format "January 2, 2006" }}Hugo 的页面变量(如.Date、.Lastmod)本身就是time.Time值,可以直接传入。
可解析的日期/时间字符串格式
以下格式(摘自 docs/content/en/_common/parsable-date-time-strings.md)都可以直接作为time.Format的输入:
| 格式 | 时区 |
|---|---|
2023-10-15T13:18:50-07:00 | America/Los_Angeles |
2023-10-15T13:18:50-0700 | America/Los_Angeles |
2023-10-15T13:18:50Z | Etc/UTC |
2023-10-15T13:18:50 | 默认Etc/UTC |
2023-10-15 | 默认Etc/UTC |
15 Oct 2023 | 默认Etc/UTC |
后三个示例没有完全限定时区,将默认使用Etc/UTC时区。
时区确定顺序
原文档明确指出,时区的确定遵循以下优先级:
- 日期/时间字符串中自带的时区偏移(如
-07:00、Z) - 项目配置中指定的
timeZone Etc/UTC时区
在 Hugo 源码中,该配置字段定义于 config/allconfig/allconfig.go 的TimeZone string,并在 config/allconfig/allconfig.go 中传给每个语言配置。配置文件示例(hugo.toml):
timeZone = "America/Los_Angeles"若模板中传入的时间字符串带有时区偏移,则偏移优先于配置;否则使用配置的timeZone;两者都没有时才回退到Etc/UTC。
布局字符串(Layout string)
time.Format的布局基于 Go 的参考时间:
Mon Jan 2 15:04:05 MST 2006布局组件(摘自 docs/content/en/_common/time-layout-string.md):
| 描述 | 有效组件 |
|---|---|
| 年 | "2006" "06" |
| 月 | "Jan" "January" "01" "1" |
| 星期 | "Mon" "Monday" |
| 日(月内) | "2" "_2" "02" |
| 日(年内) | "__2" "002" |
| 时 | "15" "3" "03" |
| 分 | "4" "04" |
| 秒 | "5" "05" |
| 上午/下午标记 | "PM" |
| 时区偏移 | "-0700" "-07:00" "-07" "-070000" "-07:00:00" |
若希望 UTC 时区输出Z而非偏移量,可将布局中的符号替换为Z:
| 描述 | 有效组件 |
|---|---|
| 时区偏移(Z 形式) | "Z0700" "Z07:00" "Z07" "Z070000" "Z07:00:00" |
组合示例:
{{ $t := "2023-01-27T23:44:58-08:00" }} {{ $t = time.AsTime $t }} {{ $t = $t.Format "Jan 02, 2006 3:04 PM Z07:00" }} {{ $t }} → Jan 27, 2023 11:44 PM -08:00时区缩写与时区偏移的区别
原文档特别强调了两点易混淆的概念:
- 字符串如
PST、CET不是时区,而是时区缩写(abbreviations) - 字符串如
-07:00、+01:00不是时区,而是时区偏移(offsets) - 时区是拥有相同本地时间的地理区域。例如
PST与PDT(视夏令时而定)所对应的时区是America/Los_Angeles
本地化(Localization)
使用time.Format可以为当前语言和区域本地化time.Time值。有两种方式:
方式一:直接使用本地化令牌
{{ .Date | time.Format ":date_medium" }} → Jan 27, 2023方式二:使用布局字符串
布局字符串同样支持本地化。原理是:Hugo 先把time.Time按布局格式化,再将英文的月份/星期名称替换为本地化名称(见下文源码解析)。
本地化令牌及示例结果
本地化为 en-US(美国英语):
| 令牌 | 结果 |
|---|---|
:date_full | Friday, January 27, 2023 |
:date_long | January 27, 2023 |
:date_medium | Jan 27, 2023 |
:date_short | 1/27/23 |
:time_full | 11:44:58 pm Pacific Standard Time |
:time_long | 11:44:58 pm PST |
:time_medium | 11:44:58 pm |
:time_short | 11:44 pm |
本地化为 de-DE(德语):
| 令牌 | 结果 |
|---|---|
:date_full | Freitag, 27. Januar 2023 |
:date_long | 27. Januar 2023 |
:date_medium | 27.01.2023 |
:date_short | 27.01.23 |
:time_full | 23:44:58 Nordamerikanische Westküsten-Normalzeit |
:time_long | 23:44:58 PST |
:time_medium | 23:44:58 |
:time_short | 23:44 |
locale 配置与语言回退
日期、货币、数字和百分比的本地化由bep/golocales包执行。Hugo 使用locale配置确定区域设置,若未设置则回退到语言键本身,解析结果必须是该包支持的 locale(详见 docs/content/en/_common/functions/locales.md)。
源码级实现原理
Format 方法:先转换,后格式化
核心实现在 tpl/time/time.go:
func (ns *Namespace) Format(layout string, v any) (string, error) { t, err := htime.ToTimeInDefaultLocationE(v, ns.location) if err != nil { return "", err } return ns.timeFormatter.Format(t, layout), nil }关键点:
- 输入
v为any类型,统一经htime.ToTimeInDefaultLocationE转换为time.Time - 转换时使用
ns.location(即项目配置的时区)作为默认位置,这正是"配置时区优先于Etc/UTC"的实现基础 - 转换支持多种输入形态:
AsTimeProvider(如 go-toml 的LocalDate/LocalDateTime)、time.Time(会先格式化为 RFC3339 再交给cast包)、以及普通字符串/时间戳
TimeFormatter:自定义布局与本地化替换
本地化替换逻辑位于 common/htime/time.go:
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.FormatDateFull(t) // ... date_long / date_medium / date_short // ... time_full / time_long / time_medium / time_short } } s := t.Format(layout) monthIdx := t.Month() - 1 dayIdx := t.Weekday() if strings.Contains(layout, "January") { s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthsWide()[monthIdx]) } else if strings.Contains(layout, "Jan") { s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthsAbbreviated()[monthIdx]) } if strings.Contains(layout, "Monday") { s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdaysWide()[dayIdx]) } else if strings.Contains(layout, "Mon") { s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdaysAbbreviated()[dayIdx]) } return s }这段代码印证了两个实现事实:
- 以
:开头的布局被识别为 Hugo 自定义本地化令牌,直接调用bep/golocales翻译器的FormatDateFull、FormatTimeShort等方法 - 普通布局字符串先由 Go 标准库
time.Format格式化,再把英文月份名(January/Jan)和星期名(Monday/Mon)替换为当前 locale 对应的翻译;空布局返回空字符串
Namespace 的构造依赖
time命名空间通过 tpl/time/time.go 的New构造,接收htime.TimeFormatter(携带 locale 翻译器)、*time.Location(默认时区)和deps.Deps,并使用dynacache缓存时区加载结果(/tmpl/time/in分区,ClearNever策略),用于time.In等函数。
测试验证
common/htime/time.go 中的英文名称切片与替换逻辑,在 tpl/time/time_test.go 中有完整测试覆盖,可验证以下行为:
- 自定义布局与字符串输入:
{"Monday, Jan 2, 2006", "2015-01-21", "Wednesday, Jan 21, 2015"} - 非日期布局字符串原样返回:
{"This isn't a date layout string", "2015-01-21", "This isn't a date layout string"} - RFC3339 与 RFC1123 格式互转
- 本地化令牌:
{":date_medium", "2015-01-21", "Jan 21, 2015"} - 时区感知:使用
America/Los_Angeles时,time.Format(":time_full", "2020-03-09T11:00:00")输出11:00:00 am Pacific Daylight Time(对应 Issue #9084)
实战组合示例
博客文章页面的日期展示
<time datetime="{{ .Date | time.Format "2006-01-02T15:04:05Z07:00" }}"> {{ .Date | time.Format "January 2, 2006" }} </time>多语言站点按 locale 输出
{{ if eq site.Language.LanguageCode "de-DE" }} <p>{{ .Date | time.Format ":date_long" }}</p> {{ else }} <p>{{ .Date | time.Format "January 2, 2006" }}</p> {{ end }}首页"最后更新时间"提示
<p>Last updated: {{ .Lastmod | time.Format "2 Jan 2006 15:04 MST" }}</p>小结
time.Format是 Hugo 模板中处理日期时间输出的核心函数:它接受time.Time值或可解析字符串,遵循"字符串偏移 > 项目timeZone配置 >Etc/UTC"的时区优先级;布局基于 Go 参考时间Mon Jan 2 15:04:05 MST 2006;以:开头的本地化令牌(如:date_medium、:time_full)可输出贴合当前 locale 的语言化结果。理解其源码实现(转换→格式化→本地化替换)后,你可以在多语言与多时区场景下精准掌控日期时间的呈现。
【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考