time.now() 返回的不是北京时间,而是运行环境当前时区的时间,在 docker、ci/cd 或 alpine 镜像中通常为 utc;需通过安装 tzdata、挂载宿主机时区或显式加载 asia/shanghai 时区来修正。

time.Now() 返回的到底是不是北京时间?
不是,它返回的是运行环境当前时区的时间,而这个“当前时区”在 Docker、CI/CD 或 Alpine 镜像里大概率是 UTC。你本地开发机显示 +0800 CST,容器里却打印 +0000 UTC,不是代码错了,是 /usr/share/zoneinfo/Asia/Shanghai 文件根本不存在。
- 别信
TZ=Asia/Shanghai环境变量——Go 的time包完全忽略它 - 检查真实时区:打印
time.Now().Location().String(),不是看Format输出里的偏移量 - Docker 构建时加
apk add --no-cache tzdata(Alpine)或apt-get install -y tzdata(Debian) - 启动容器时挂载宿主机时区:
-v /etc/localtime:/etc/localtime:ro,比环境变量更可靠
解析前端传来的 "2026-07-01 14:30:00" 该用 Parse 还是 ParseInLocation?
必须用 ParseInLocation。字符串不含时区信息时,Parse 默认按 time.UTC 解析,不是你期望的东八区——这就是最隐蔽的 8 小时偏差源头。
- 先加载时区:
sh, err := time.LoadLocation("Asia/Shanghai"),务必检查err != nil - 再解析:
time.ParseInLocation("2006-01-02 15:04:05", s, sh) - 布局字符串年份必须是
2006,写成"2026-01-02"会直接返回零值和错误 - 如果字符串本身带偏移(如
"2026-07-01T14:30:00+08:00"),layout 中对应位置得用-07:00占位
GORM 写入 MySQL 后时间少了 8 小时,问题出在哪?
不是 GORM 的 bug,是 Go 运行时、MySQL 服务端、数据库连接驱动三者时区没对齐。典型链路:代码里 time.Now() 是北京时间 → 驱动按本地时区发给 MySQL → MySQL 用自身默认时区(通常是 UTC)存入 DATETIME 字段 → 查出来自然晚 8 小时。
- 统一锚点为 UTC 是最稳方案:启动时设
time.Local = time.UTC(需在所有 goroutine 启动前) - DSN 加参数:
parseTime=true&loc=UTC,否则database/sql驱动不解析时区 - 避免混用:
time.Local在多时区服务里是陷阱,业务需要上海时间就显式用sh.In(t) - 读取后别直接
t.Format,先确认字段原始语义:数据库存的是 UTC 就用t.UTC().Format,存的是本地时间才用t.In(sh).Format
API 返回 JSON 时间字段,为什么前端显示还是错?
因为 time.Time.MarshalJSON 默认输出 ISO8601 格式(如 "2026-07-01T14:30:00+08:00"),但前提是原始 time.Time 的 Location 正确。如果入库时就错了,JSON 里带的偏移量也是错的。
- 别在 API 层做
t.UTC().Format再塞进 struct——这会丢掉原始时区上下文 - 确保结构体字段是
time.Time类型,且赋值前已通过In(sh)或UTC()显式归一 - 文档必须写明字段含义,例如:
"created_at": "2026-07-01T14:30:00+08:00"表示东八区时间 - 日历逻辑(如“今天 0 点”)不能依赖
time.Now().Truncate(24*time.Hour),得基于业务时区构造:sh.Now().Truncate(24*time.Hour)
go run -e 'package main; import "time"; func main() { println(time.Now().Location()) }' 确认时区加载状态,比事后查日志快十倍。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











