gtime 时间处理与 MySQL 时区交互
时间字段是后端最容易踩坑的地方:同样的“2024-01-02 10:00:00”,程序时区、数据库时区、驱动转换各差一步,读出来就可能差 8 小时。GoFrame v2 提供 gtime 组件封装时间对象,并在 ORM 时区处理章节专门讲解了与 MySQL 的交互,本章把它们串起来讲。
gtime 基本使用
gtime.Time 底层仍是 time.Time,但补齐了格式化、解析、时区转换的便捷方法:
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gtime"
)
func main() {
// 当前时间
now := gtime.Now()
fmt.Println(now.String()) // 2024-01-02 10:00:00
// 从字符串解析(自动识别常见格式)
t1, err := gtime.StrToTime("2024-01-02 15:04:05")
if err != nil {
panic(err)
}
fmt.Println(t1.Timestamp()) // 秒级时间戳
// 时间戳 -> 时间对象
t2 := gtime.NewFromTimeStamp(t1.Timestamp())
// 格式化输出:gtime 支持 Y-m-d H:i:s 风格格式
fmt.Println(t2.Format("Y-m-d H:i:s"))
}
gtime.Time 常用方法还包括 Add/AddDate(时间运算)、ToLocation(loc)(时区转换)、Format、Timestamp 等;时间对象直接 json 序列化时输出为“2006-01-02 15:04:05”文本,前端解析友好。
全局时区设置
进程的全局时区影响所有时间计算(gcron 调度、日志时间、ORM 默认值),官方提供 gtime.SetTimeZone("Asia/Shanghai"):
func init() {
// 只能全局设置一次;再次调用且时区不一致会返回 error
if err := gtime.SetTimeZone("Asia/Shanghai"); err != nil {
panic(err)
}
}
注意官方反复提醒:标准库 time 包在 import 初始化时会固定时区,因此业务项目里建议把 SetTimeZone 放到独立包中,并在 main 的最前面 import 它(import _ "project/boot/time"),否则 time 包已初始化会导致设置失效,此时只能对单个对象用 ToLocation 转换。
与 MySQL 时间字段交互
ORM 底层使用 go-sql-driver/mysql,其 loc 参数决定 time.Time 提交时的时区换算。官方建议在连接串统一加 loc=Local&parseTime=true:
database:
default:
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test?loc=Local&parseTime=true"
debug: true
// 推荐:用 time.Local 构造,与 loc=Local 一致,不产生偏移
t, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-01-02 10:00:00", time.Local)
g.DB().Model("user").Ctx(ctx).Where("created_at>?", t).One()
// 坑:time.Parse 得到 UTC 时间,会被驱动按 loc 换算成 +8 再入库,
// 导致条件时间偏差 8 小时,除非你确实想要这种转换
同理,读库结果映射为 *gtime.Time 时带上了 loc=Local 语义;若连接串不带 loc,驱动按 UTC 处理,多时区部署时会得到与预期不一致的值。
gen dao 与时间字段类型
gf gen dao 默认把表的时间/日期字段生成为 *gtime.Time 属性;若项目坚持使用标准库 time.Time,在 gfcli.gen.dao 配置中设 stdTime: true 即可。两种选择都有官方支持,关键是与 ORM 时区处理保持一致的理解。
注意点
SetTimeZone只能成功设置一次,多次调用可能报错。- 表里的
created_at/updated_at等由数据库DEFAULT CURRENT_TIMESTAMP维护的字段,写入时不要手工塞time.Time,交给数据库更省心(需确认表结构与 ORM 时间维护配置是否冲突,以官方文档为准)。 - 服务器时区、Docker 容器时区、数据库时区三者建议统一为同一时区,问题少一半。
小结
gtime 解决“对象好用、格式统一”,loc=Local&parseTime=true 解决“驱动转换一致”,SetTimeZone 解决“进程全局时区”。记住三条主线,时间字段的八小时问题基本可以避免。组件细节以官方文档 goframe.org 为准。