Go中两个time.Time相减必须用Sub方法,返回time.Duration;Duration不支持年月等历法计算,需手动处理时区、闰年等边界情况。

用 Sub 得到 Duration,不是“减法运算符”
Go 里两个 time.Time 不能直接用 - 相减,必须调用 Sub 方法。这是初学者最常卡住的地方——编译报错 invalid operation: t1 - t2 (operator - not defined on time.Time) 就是因为这个。
-
Sub返回的是time.Duration(纳秒级整数),不是另一个Time - 顺序很重要:
t1.Sub(t2)表示 “t1 距 t2 过去了多久”,结果为正说明t1在t2之后 - 如果只关心绝对天数,别忘了取绝对值:
int(math.Abs(d.Hours() / 24))(需导入math)
算“天数”别直接除 24,小心浮点误差和时区偏移
用 d.Hours() / 24 看似合理,但隐含两个风险:一是 Hours() 返回 float64,微小舍入可能让 int() 截断出错;二是若两个时间点带不同时区或非零时分秒,直接除会引入非整日偏差。
- 更稳妥的做法是归一化到零点再算:
t1.Truncate(24*time.Hour).Sub(t2.Truncate(24*time.Hour)).Hours() / 24 - 或者用秒数整除:
int(d.Seconds() / 86400)(86400 * time.Second更 Go 风格) - 如果原始字符串没带时区,务必用
time.ParseInLocation指定time.UTC或本地时区,否则默认按Local解析,跨时区对比会出错
别指望 Duration 直接给出“年”或“月”
Go 的 time.Duration 是纯线性时间量(纳秒),它**不理解闰年、大小月、夏令时这些历法规则**。所以你无法从 d := t1.Sub(t2) 中安全提取“几年几个月”——这属于语义化日历计算,不是时间差。
- 想算“2023-02-28 到 2024-02-28 是不是整一年?”——不能靠
Duration,得用t1.Year() - t2.Year()等字段手动比对,并处理 2 月 29 日等边界 - 第三方库如
github.com/alexedwards/argon或手写逻辑才能做日历差,标准库只保证“总纳秒数”精确 - 常见误用:把
d.Hours() / (24 * 365)当作年数——闰年会让它每年漂移约 0.24 天
解析字符串时格式写错,Parse 不报错但结果是零值
time.Parse("2006-01-02", s) 如果 s 是 "2025-13-01" 这种非法日期,函数不会 panic,而是返回 time.Time{}(即零时间)和一个非 nil error。后续调用 Sub 会得到极大负数,极难排查。
立即学习“go语言免费学习笔记(深入)”;
- 永远检查
err:不是_, err := time.Parse(...),而是t, err := time.Parse(...); if err != nil { ... } - 测试用例至少覆盖:闰年 2 月 29 日、13 月、32 日、空字符串、时区缩写(如 "PST")
- 若输入来自用户,建议先用正则粗筛(如
^\d{4}-\d{2}-\d{2}$),再进Parse
真正麻烦的从来不是“怎么算差”,而是“怎么确保两个时间点在同一个语义上下文里”——时区、零点对齐、非法输入、历法含义,每个都可能让看似正确的代码在线上跑出离谱结果。


















