Beego ORM中time.Time字段默认按time.Local解析,但Local依赖运行环境时区,易导致跨环境时间偏移;应统一设orm.DefaultTimeLoc=time.UTC、MySQL连接加&loc=UTC,并避免直接传字符串时间给Filter。

Beego ORM 里 time.Time 字段默认按 Local 时区解析
Beego ORM 默认用 time.Local 解析字符串时间,比如你传 "2026-08-21 10:00:00" 给 Filter("created_at__gt", "2026-08-21 10:00:00"),它会调用 time.ParseInLocation(formatDateTime, s, DefaultTimeLoc) —— 这里的 DefaultTimeLoc 初始值就是 time.Local。但问题在于:这个 “Local” 是运行时所在机器的本地时区,不是你数据库的时区,也不是你业务期望的时区。
常见错误现象:
- 开发机是 CST(+0800),测试环境是 UTC(+0000),同样的字符串时间条件,查出来的记录数不同
-
o.QueryTable("log").Filter("ts__gt", "2026-08-21 00:00:00").Count()在 UTC 环境下实际生成的 SQL 条件变成ts > '2026-08-21 08:00:00'
MySQL 驱动层 loc 参数必须显式设置,否则默认走 UTC
Go 的 github.com/go-sql-driver/mysql 驱动在处理 time.Time 类型参数时,依赖连接串里的 loc 参数做转换。不加 loc=,驱动内部就用 time.UTC 作为目标时区,和 Beego ORM 的 DefaultTimeLoc 完全脱节。
实操建议:
- 连接字符串末尾必须加上
&loc=Asia/Shanghai(或&loc=Local,但Local行为受系统影响大,不推荐) - 不要只写
loc=Local:它依赖os.Getenv("TZ")或系统配置,Docker 容器里常为空,退化成 UTC - 如果数据库服务器时区是 UTC,应用统一用 UTC 存取,连接串设
&loc=UTC,同时全局设orm.DefaultTimeLoc = time.UTC
字符串时间过滤前,先转成带时区的 time.Time
绕过 Beego ORM 自动解析字符串的逻辑,是最稳妥的做法。ORM 对字符串的解析逻辑固定且不可配置(见 db_utils.go#getFlatParams),一旦环境时区不一致,结果就不可控。
正确做法是:你自己解析字符串,明确指定时区,再传给 Filter:
t, _ := time.ParseInLocation("2006-01-02 15:04:05", "2026-08-21 10:00:00", time.Local)
o.QueryTable("event").Filter("ts__gt", t).Count()
这样做的好处:
- Beego ORM 不再对字符串做二次解析,直接把
time.Time值交给 MySQL 驱动 - 驱动根据连接串
loc=参数决定如何序列化该值,行为可预测 - 避免
Filter("ts__gt", "2026-08-21 10:00:00")这类写法在跨环境部署时突然偏移 8 小时
auto_now_add / auto_now 字段也要注意时区一致性
Beego ORM 的 auto_now_add 和 auto_now 是在 Go 进程内生成时间戳,用的是 time.Now(),也就是 time.Local。如果数据库字段类型是 TIMESTAMP(自动转时区),而 MySQL server 的 time_zone 是 +00:00,那插入的值就会被 MySQL 自动转成 UTC 时间,和 Go 写入的本地时间对不上。
建议:
- 数据库时间字段优先用
DATETIME类型(不自动时区转换),配合统一时区策略 - 若必须用
TIMESTAMP,确保 MySQL 的time_zone和应用层loc、DefaultTimeLoc三者一致 - 上线前检查 MySQL:
SELECT @@global.time_zone, @@session.time_zone;
time.Local、MySQL time_zone、驱动 loc 这三者没对齐,且任一环节缺失显式声明。


















