MySQL时区问题
MySQL 有两个时区参数,一个是“MySQL 服务所在服务器的时区——system_time_zone”,另一个是“MySQL 使用的时区——time_zone”。
使用下面命令可以查询这两个参数值:
1  | mysql> show global variables like '%time_zone%';  | 
system_time_zone 的值取的是 MySQL 服务启动时的所在服务器的时区,若服务器的时区发生变化,重启 MySQL 后 system_time_zone 值也会变化。该值有时为空,但为空时也与启动时的所在服务器的时区一致。
time_zone 代表“MySQL 使用的时区”,它的值为 SYSTEM 说明它和 system_time_zone 保持一致,即它也取的是 MySQL 服务启动时的所在服务器的时区。若在mysql.ini文件中设置default-time-zone参数,则它就使用这个参数指定的时区:
1  | [mysqld]  | 
MySQL 服务运行时,可以设置global time_zone(全局时区)和session time_zone(当前session时区)来修改 MySQL 使用的时区。其中,修改global time_zone(全局时区)即修改了time_zone的值,会影响所有新创建的 session 的时区,直到 MySQL 重启后才会变成 MySQL 启动时的时区。
1  | --修改全局时区  | 
session time_zone(当前session时区)就是当前连接 MySQL 会话使用的时区,默认就是 time_zone 的时区。连接 MySQL 会话的例子如在 Navicat 中打开一个查询框,一个查询框就是一个连接 session。还有,JDBC 连接也是一个 session,JDBC 连接配置中的serverTimezone参数就是设置 Java 应用连接 MySQL 的 session 使用的时区,它的优先级比 time_zone 高。
1  | spring:  | 
JDBC 这样配置后,把 Date 数据插入 MySQL 前,Java 应用依赖的 MySQL 驱动会先将它转为 UTC 时间再插入到 MySQL 的数据类型 datetime or timestamp 中。这两种数据类型的区别只有:timestamp 的显示会按照 MySQL 时区(time_zone),datetime 就显示插入的值。
举例:
若 MySQL 时区 time_zone 与系统时区(GMT8)一致,Java 应用中向 MySQL 插入new Date()对象值为“Sun Mar 19 16:24:59 CST 2023”,步骤为:
- 由于serverTimeZone配置的是 UTC 时间,MySQL 驱动会将这个Date对象转为字符串值“2023-03-19 08:25:00”放到 sql 中,再将sql发送给 MySQL,因为Date对象本身是无法走网络的。
 - MySQL 数据库接收到这个时间字符串“2023-03-19 08:25:00”后,由于数据库时区配置 time_zone = GMT8,它会认为这个时间所在的时区是 GMT8 的,保存到数据库中的时间就是 GMT8 的“2023-03-19 08:25:00”,而这跟我们实际的时间“Sun Mar 19 16:24:59 CST 2023”不一致。
 
当 Java 程序中查询这个时间时看到的是:“datetime=Sun Mar 19 16:25:00 CST 2023, timestamp=Sun Mar 19 16:25:00 CST 2023”,和插入的字符串值“2023-03-19 08:25:00”不一致,原因是 Java 程序在从 MySQL 中取到字符串值“2023-03-19 08:25:00”后将它理解为 UTC 时间(serverTimezone 指定的),而 Java 程序所在系统时间是 GMT8 ,所以展示时将这个 UTC 时间转化成了 GMT8 时间展示。这样恰好没出错,但实际上 MySQL 中存储的是 GMT8 的“2023-03-19 08:25:00”,而 Java 应用却认为 MySQL 存储的是 UTC 的“2023-03-19 08:25:00”,一旦将 JDBC 的 serverTimezone 修改,这种侥幸就被打破了。
所以,JDBC 的 serverTimezone 应和 MySQL 的 time_zone 保持同步。
Spring序列化的时区问题
用程序查询出的时间与系统时区(GMT8)一致,但前端收到的响应却是:
1  | {"id":14, "dateTime":"2020-11-08T10:01:25.000+00:00", "timeStamp":"2020-11-08T10:01:25.000+00:00"}  | 
两个时间都是 UTC,为什么?
答:Spring 将响应实体类转为 JSON 格式时,默认用 Jackson 框架,在处理 Date 类型的字段时,Jackson 框架默认的时区是 GMT0