time.sub 返回绝对时间差,需确保时区一致;推荐统一转为 utc 或本地时区再计算;duration 不支持 days(),取整天数应使用 int(delta.hours()/24);年月日跨度属日历运算,需业务逻辑处理。

用 time.Sub 算时间差,但必须确保时区一致
Go 的 time.Sub 返回的是两个 time.Time 值之间的物理纳秒差,它不关心“年月日”语义,只认绝对时间点。所以如果你拿一个本地时间(比如 time.Now())和一个 UTC 时间(比如 time.Date(..., time.UTC))直接相减,结果会包含时区偏移,不是你想要的“真实经过时间”。
- 常见错误现象:
d1.Sub(d2)得到 -5h 或 +8h 的“意外”偏移,尤其在解析字符串后没显式指定时区时 - 正确做法:统一转成同一时区再算,推荐都用
time.UTC或都用本地时区;若需保留原始时区上下文,用time.ParseInLocation显式加载 - 示例:
loc, _ := time.LoadLocation("Asia/Shanghai"); t1, _ := time.ParseInLocation("2006-01-02", "2023-01-01", loc); t2, _ := time.ParseInLocation("2006-01-02", "2023-01-10", loc); delta := t2.Sub(t1)
Duration 能转小时/分钟,但“天数”要小心处理
time.Duration 是纳秒级精度的差值类型,.Hours()、.Minutes() 返回 float64,而 .Seconds() 是 float64,.Nanoseconds() 是 int64。但“多少天”没有内置整数方法——因为一天不总是 24 小时(夏令时切换时可能 23 或 25 小时),所以 Go 不提供 .Days()。
- 安全取整天数:
int(delta.Hours() / 24)(向下取整,适合统计“过了几天”) - 四舍五入天数:
int(math.Round(delta.Hours() / 24))(需import "math") - 精确到秒的总天数:
delta / (24 * time.Hour),结果仍是time.Duration,可转int64;注意这是截断除法,非四舍五入 - 别用
delta / 86400e9手动算——虽然数值对,但可读性差且易出错
别指望 Sub 给出“几年几个月”,那是业务逻辑不是时间差
time.Sub 永远只返回一个标量 time.Duration,它不会、也不能告诉你“相差 2 年 3 个月 5 天”,因为月份长度不固定(28–31 天)、闰年规则复杂,这种计算依赖起始日期的具体年月日,属于日历运算(calendar arithmetic),不是时间间隔(elapsed time)。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 如果你真需要“用户友好的跨度”,得自己写逻辑或用第三方库(如
github.com/alexcesaro/date),但要注意:这类结果是近似值,不是精确物理时长 - 典型误用:
t2.Sub(t1).Hours() / (365.25 * 24)算“年份”——这忽略了闰月、夏令时、甚至跨世纪历法规则,仅适用于粗略估算 - 真正可靠的做法:用
t1.AddDate(y, m, d)反向试探,或按业务需求定义“年=365天”“月=30天”并明确文档说明
解析字符串时,time.Parse 默认按 UTC,不加时区就容易错
当你用 time.Parse("2006-01-02", "2023-01-01"),Go 默认把该时间解释为 UTC 时间点,哪怕你本地是 CST。如果后续和 time.Now()(本地时区)相减,差值就会多出 8 小时(或对应偏移)。
- 最稳妥方式:统一用
time.ParseInLocation,显式传入time.Local或具体时区(如time.UTC) - 调试技巧:打印
t.Location().String()和t.Format(time.RFC3339),确认时区是否符合预期 - 小陷阱:
time.Date(2023, 1, 1, 0, 0, 0, 0, time.Local)创建的是本地时区零点,但time.Parse不会自动识别这个上下文——它只看字符串和 layout
真正难的从来不是“怎么算差”,而是“你到底想算什么差”:是物理流逝的纳秒,还是日历上的年月日跨度?前者用 Sub 没问题,后者就得跳出 time.Duration 思维,自己定义规则。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










