t2.sub(t1) 返回 time.duration 类型的纳秒级差值,是计算两个 time.time 差值的唯一推荐方式;duration 本质是 int64 纳秒数,需手动格式化为可读单位,不支持直接乘除或 json 字符串序列化。

用 Sub 计算两个 time.Time 的差值,结果是 time.Duration
Go 里没有“时间差”类型,只有 time.Duration —— 它本质是纳秒数的 int64,不是时间点。所以 t2.Sub(t1) 返回的是从 t1 到 t2 经过了多少纳秒(正负取决于顺序)。
常见错误:把 Duration 当成可直接打印的“中文描述”,比如期待输出“2小时30分钟”,但实际它只存数字。需要手动转换单位或格式化。
-
t2.Sub(t1)是唯一推荐方式;别用t2.Unix() - t1.Unix(),会丢掉纳秒精度,且跨闰秒时行为不可靠 - 如果
t1晚于t2,结果为负,Duration支持负值,比如-30m - 注意时区:只要两个
time.Time都带时区(比如都用time.Now()),Sub自动按 UTC 纳秒差计算,无需手动转换
怎么把 Duration 转成“几小时几分钟”这种可读格式
Go 标准库不提供自动中文或带单位的字符串格式化,Duration.String() 只输出类似 2h30m0.123s 的紧凑形式,且对小于 1 秒的值会显示纳秒,不易读。
真正常用的是拆解成整数单位再拼接,比如小时、分钟、秒:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
dur := t2.Sub(t1)
hours := int64(dur.Hours())
minutes := int64(dur.Minutes()) % 60
seconds := int64(dur.Seconds()) % 60
fmt.Printf("%dh %dm %ds", hours, minutes, seconds)
- 别用
dur / time.Hour做整除——虽然可行,但Hours()更语义清晰,且内部做了浮点转整处理 - 如果要支持“毫秒级”显示,用
dur.Milliseconds()%1000,但注意Milliseconds()会截断小数毫秒,纳秒精度丢失 - 负
Duration传给Hours()会返回负数,取模前记得先abs或判断符号
为什么 Duration 不能直接和 time.Time 相加减以外的运算
Duration 是纯数值类型,不带单位上下文。它能做加减(t.Add(d)、t.Sub(t)),但不能做乘除、比较大小(除非转成 int64)、也不能直接参与 JSON 序列化(默认序列化成纳秒整数,前端难理解)。
- JSON 场景下,别直接 struct tag 用
json:",string"试图让Duration输出字符串——它不会生效;得自定义MarshalJSON方法,返回你想要的格式(如"2h30m") - 数据库存储时,通常存为 int64(纳秒或秒),而不是字符串;否则排序、范围查询会失效
- 别写
if dur > 5 * time.Minute这种代码——可以,但可读性差;建议写成if dur > 5*time.Minute,乘号前后留空格,Go 官方示例都这么写
时区不同、Location 不一致时 Sub 还准不准
准。只要两个 time.Time 的 Location 不同,Sub 仍以它们各自对应的 UTC 时间为准计算纳秒差。比如一个在东京时间、一个在纽约时间,只要时间值本身正确,差值就是真实经过的物理时间。
- 错误做法:先
t.In(time.UTC)再Sub——多余,Sub内部已经这么做了 - 容易忽略的坑:用
time.Parse解析字符串时没指定Location,导致解析出的Time默认是Local,而你本地时区和服务器不一致,差值就偏了 8 小时 - 稳妥做法:统一用
time.ParseInLocation,显式传入time.UTC或业务约定的时区
时间差计算本身很简单,但真正卡住人的永远是后续怎么展示、怎么存、怎么跨系统传递——Duration 不是“人话”,它只是个数字,所有“好读”的逻辑都得你自己补。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










