time.Sub 是唯一可靠方式,需统一时区、检查解析错误、用整数除法转换 Duration;避免 Unix 时间差、浮点比较及日历语义误用。

Go 里算两个时间差,time.Sub 是唯一可靠入口,但直接用它不等于结果就对——时区不一致、字符串解析没指定位置、Duration 转整数时用错方法,三者任一出错,差值就悄悄偏了几个小时甚至几天。
time.Sub 是唯一合法方式,别试 - 运算符
Go 的 time.Time 类型不支持直接相减:t1 - t2 编译报错 invalid operation: t1 - t2 (operator - not defined on time.Time)。必须调用 t2.Sub(t1)。
- 顺序决定正负:
later.Sub(earlier)得正数,earlier.Sub(later)得负time.Duration - 返回值是纳秒级
int64,底层无浮点误差,但显示为2h30m15.123s这类字符串只是格式化,不影响精度 - 别用
t2.Unix() - t1.Unix()替代——会丢掉纳秒,且跨闰秒时行为未定义
时区不统一,Sub 结果就是错的
常见错误现象:t2.Sub(t1) 返回 -5h 或 +8h 的“意外”偏移,尤其当一个来自 time.Now()(本地时区),另一个来自 time.Parse("2006-01-02", s)(默认 UTC)时。
-
time.Parse不带时区参数时,默认按time.UTC解析;而time.Now()默认是time.Local—— 两者相减实际算的是 UTC 偏移差,不是钟表时间差 - 调试时加一句
fmt.Println(t1.Location(), t2.Location()),确认输出都是UTC或都是Local - 推荐统一转 UTC:
t1.UTC().Sub(t2.UTC()),或解析时强制指定:time.ParseInLocation(layout, s, time.UTC)
Duration 转天数/小时要防浮点陷阱
duration.Hours() 返回 float64,直接用于整数比较或取整极易出错——纳秒转浮点可能丢失精度,24 * time.Hour 的精确值是 86400000000000 纳秒,但 duration.Hours() 可能是 23.999999999999996。
立即学习“go语言免费学习笔记(深入)”;
- 要整数天数:用
int(duration / (24 * time.Hour))(截断除法),不是int(duration.Hours() / 24) - 要毫秒整数(Go 1.19+):
duration.Milliseconds();老版本用int64(duration / time.Millisecond) - 做阈值判断(如超时):
if duration > 5 * time.Minute安全;但别写if duration.Seconds() > 300,浮点比较不可靠
别指望 Sub 给出“几年几个月”,那是日历逻辑
time.Duration 是纯线性时间量,不理解闰年、大小月、夏令时。它能告诉你总纳秒数,但无法回答“2023-02-28 到 2024-02-28 是不是整一年?”
-
duration.Hours() / (24 * 365)算年份?2024 是闰年,每年漂移约 0.24 天 - 需要“X 年 Y 月 Z 日”语义?得用
t1.AddDate(y, m, d)反向试探,或手动比对t1.Year()、t1.Month()等字段 - 字符串解析失败时返回零值时间(
1970-01-01),Sub结果极大且难排查——永远检查err,别用_忽略
真正容易被忽略的,是时区和错误处理这两环:一个漏掉 .UTC(),一个漏掉 if err != nil,差值就从毫秒级误差变成几小时偏差,而且日志里几乎看不出异常。


















