
go 的 time.format() 不支持自动添加日期序数后缀(如 1st、2nd、3rd),需手动拼接;本文提供简洁可靠的自定义格式化函数,并涵盖边界处理与本地化注意事项。
Go 标准库的 time.Time.Format() 方法基于固定布局字符串(如 "2006-01-02")进行解析,其占位符(如 "2" 表示日)仅控制数字宽度与位置,不支持动态后缀逻辑(例如将 1 → "1st"、2 → "2nd")。因此,直接使用 "Monday 2nd January" 会导致字面量 "nd" 被原样输出,造成 4nd、1nd 等错误结果。
要实现符合英语习惯的“verbose date”(如 Wednesday 1st March),需分离日期数值与后缀逻辑,再组合格式化字符串。以下是一个健壮、可读性强的实现:
func formatVerboseDate(t time.Time) string {
day := t.Day()
var suffix string
switch {
case day%10 == 1 && day != 11:
suffix = "st"
case day%10 == 2 && day != 12:
suffix = "nd"
case day%10 == 3 && day != 13:
suffix = "rd"
default:
suffix = "th"
}
// 使用 Format() 渲染静态部分,动态插入后缀
return t.Format("Monday 2") + suffix + t.Format(" January 2006")
}✅ 关键改进说明:
使用 day%10 配合排除 11/12/13 的条件判断,正确覆盖所有月份(包括 31 天的月份);
将格式串拆分为三段:"Monday 2"(含星期与日数字)、suffix(动态后缀)、" January 2006"(月与年),避免重复调用 Format() 影响性能;
-
示例输出:
t := time.Date(2025, time.March, 1, 0, 0, 0, 0, time.UTC) fmt.Println(formatVerboseDate(t)) // "Sunday 1st March 2025" t = time.Date(2025, time.March, 22, 0, 0, 0, 0, time.UTC) fmt.Println(formatVerboseDate(t)) // "Friday 22nd March 2025"
⚠️ 注意事项:
- 此方案默认使用系统本地时区或显式指定的时区(如 time.UTC),若需多语言支持(如西班牙语 "1º de marzo"),应结合 golang.org/x/text 包实现国际化;
- 不建议在高频循环中反复拼接字符串,如需极致性能,可预生成后缀映射表(map[int]string);
- 英语序数规则存在例外(如 11th, 12th, 13th),上述 switch 逻辑已正确规避——这是初学者最容易遗漏的陷阱。
总结:Go 的时间格式化强调确定性与无状态,复杂文本逻辑需由开发者显式控制。掌握这种“分离-计算-组合”模式,不仅能解决序数日期问题,也为处理其他定制化格式(如货币符号位置、时区缩写映射等)提供了通用思路。










