time.now() 返回运行环境配置的本地时区时间,但易退化为utc;格式化与解析必须用“2006-01-02 15:04:05”布局;跨月计算用adddate();无时区字符串解析须用parseinlocation()。

time.Now() 返回的到底是本地时间还是 UTC?
它返回的是你运行环境配置的本地时区时间,不是 UTC —— 但这个“本地”很脆弱。比如在 Docker 容器里没挂载 /etc/localtime 或没设 TZ=Asia/Shanghai,time.Now() 就会悄悄退化成 UTC 时间,且不报错、不警告。
- 验证方式:
fmt.Println(time.Now().Location()),输出Local不代表一定有正确偏移,要看它内部是否加载了真实时区数据 - 生产环境强烈建议显式绑定时区:
shanghai, _ := time.LoadLocation("Asia/Shanghai"),再用time.Now().In(shanghai) - 数据库存储、日志打点、跨服务通信,统一用
time.Now().UTC()更安全,显示时再转本地
格式化和解析必须用 "2006-01-02 15:04:05",不能写成实际年份
这不是占位符语法,而是 Go 硬编码的“参考时间布局(layout)”。写成 "2023-01-02" 或 "%Y-%m-%d" 都会解析失败或返回零值时间 0001-01-01 00:00:00 +0000 UTC,而 error 可能为 nil(尤其用 ParseInLocation 时),极易埋下静默 bug。
- 正确写法:
t.Format("2006-01-02 15:04:05")、time.Parse("2006-01-02", "2026-01-26") - 带毫秒:
"2006-01-02 15:04:05.000";带时区缩写:"2006-01-02 15:04:05 MST"(注意:MST 是占位符,不是字面量) - 字符串含时区偏移(如
"2026-01-26 10:30:00+0800")时,layout 必须含-0700,否则时区被忽略,按本地解释
加减天数/月份要用 AddDate(),别只靠 Add()
Add() 是基于纳秒的线性运算,对“加 1 个月”这种日历语义操作完全无效——比如 1 月 31 日 Add(30 * 24 * time.Hour) 可能落到 3 月 2 日,而不是你想要的 2 月 28 日或 29 日。
- 改用
t.AddDate(0, 1, 0):加 1 个月,自动处理大小月、闰年 -
AddDate(0, 0, 7)表示加 7 天,比Add(7 * 24 * time.Hour)更准确(避开夏令时跳变等边界) - 跨月计算后记得检查有效性:
if t.Day() != 31 { /* 原本是1月31日,加1个月后可能变成2月28日 */ }
time.Parse() 默认按 UTC 解析,ParseInLocation() 才真按你指定的时区
这是最常踩的坑:传入 "2026-01-26 10:30:00" 这种无时区字符串,time.Parse("2006-01-02 15:04:05", s) 会把它当 UTC 时间解析,然后你再调 .In(shanghai),结果就比预期快了 8 小时。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法:直接用
time.ParseInLocation("2006-01-02 15:04:05", s, shanghai) - 如果输入字符串本身带时区(如
"2026-01-26T10:30:00+08:00"),优先用time.RFC3339常量,它内置支持时区解析 - 永远检查 error:
if err != nil { /* 别忽略,time.Time{} 是个合法但无意义的零值 */ }
时间处理的复杂点不在 API 多少,而在 layout 字符串和时区绑定这两个地方——写错一个字符、漏一个时区参数,结果可能偏差数小时且难以复现。










