处理多时区的时间
Go 是怎么处理的
在 Go 的 time 库里面,使用 time.ParseInLocation 或者 time.Date 时,都需要传入 time.Location 参数,然后生成 time.Time 对象。
time.Time 底层存储的是秒数、纳秒数以及时区 time.Location,这个秒数和纳秒数的时区必须是 UTC,与 time.Location 的值无关, time.Location 仅在格式化为可读形式的时候有用。如果在上述传的 time.Location 不是 UTC,则需要对生成后的秒数进行校正。
如果在存储到数据库的时候使用这个秒数格式化为可读形式,则会发现原先 Asia/Shanghai 时区的 2021-01-05 00:00:00
变成 2021-01-04 16:00:00
,这是从 +8 时区还原到 0 时区时减去了 8 小时的秒数。
Go 的 MySQL 驱动在处理结构体里的 time.Time 对象时,会将其转换为一个已事先配置好的时区,这个默认时区是 UTC。所以如果不另外配置,就会出现上述的转换。
可以通过 DSN (Data Source Name)配置时区,格式为 user:password@tcp(192.168.1.1)/dbname?charset=utf8mb4&parseTime=true&loc=Asia%2fShanghai
后面的 loc=Asia%2fShanghai
。这里按照要求将 /
转换为 %2f
。
具体的转换是通过 time.Time 的 In() 方法实现的,它会把时区设置为 DSN 指定的时区。在转换为 SQL 时,会使用 time.Time 的 Date 和 Clock 方法。这两个方法返回的数据受到时区的影响,因此与预期的时区的展示时间一致。
使用这个方法的好处是,不需要修改数据库的配置就能在数据库里面存入展示形式为当前时区的数据,方便查看。缺点是涉及到不同时区的设备在把数据存储到数据库的时候,如果没有全部指定为同一个时区,则会出现混乱。如果指定为同一个时区,则会使得处于其他时区的人在查看数据库里的原始数据时,必须知道这是哪个时区的时间。对于与设置不同的时区的人来说,就变得不直观了。
因此如果不涉及不同时区的人员想要用他们的时区直接查看数据库原始数据,则所有的连接统一配置为当前时区,这样就不用对其他查询工具提出要求。 而如果涉及,那么则应该统一存储 UTC 时区的形式,然后在客户端配置时区,由客户端转换。