在开发 SpringBoot 项目时,表中有两个时间字段
- 一个通过 Java 代码使用
new Date()
方法获取当前时间再插入数据库- 另一个是使用 MySQL 的
CURRENT_TIMESTAMP
作为默认值实际运行时发现数据库中的这两个时间值不一致,代码插入的时间比数据库自动生成的时间早了8小时,最终发现是 yml 配置问题
在此记录下我的解决过程,如有错误,欢迎指正!
1. 问题描述
开发环境:MySQL 5.7.19、Java 8、IntelliJ IDEA 2020.3
1.1 问题详情
- Java 代码中设置用户加入时间:
user.setJoinTime(new Date());
- 数据库表的建表语句中
joinTime
和createTime
字段的定义:
joinTime datetime null
createTime datetime default CURRENT_TIMESTAMP
- yaml 中的配置:
serverTimezone=UTC
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/thr?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
- 运行代码后数据中插入数据后的时间值:
joinTime
:2024-07-14 08:49:19createTime
:2024-07-14 16:49:18
- 现象:实际插入时间是
createTime
,而joinTime
按理来说应该与createTime
的值一致,但从结果看出始终比实际时间早了八小时
1.2 原因分析
-
时区对比:
-
UTC (Coordinated Universal Time):协调世界时(UTC)是全球标准时间,作为时间的基准,不受地区时区影响
-
时差:UTC+0,无时区偏移
-
用途:广泛用于全球化的服务器和系统,以避免时区差异带来的复杂性
-
-
Asia/Shanghai (中国标准时间 CST):用于中国大陆地区
-
时差:UTC+8,比UTC晚8小时
-
用途:适用于中国本地化应用,时间存储和显示都基于北京时间,如果需要考虑1986年到1991年夏令时的历史数据,该时区会自动考虑这段时间夏令时的影响
-
-
-
查看JVM默认时区:在
Application
文件中添加代码,运行程序,输出当前时间和时区信息- 运行结果:
JVM TimeZone: Asia/Shanghai
- 运行结果:
public static void main(String[] args) {
System.out.println("JVM TimeZone: " + TimeZone.getDefault().getID());
SpringApplication.run(Application.class, args);
}
- 查看本地数据库时区:为东八区时间(北京时间)
SELECT @@global.time_zone, @@session.time_zone;
- 分析:可以看出本地数据库和JVM时区其实都是东八区,而我在yml里配置的时区是
UTC
,对照结果来看可知:joinTime
字段使用new Date()
在JVM默认时区(Asia/Shanghai)获取时间并插入,此时,JVM时间为北京时间2024-07-14 16:49:19
- 但由于数据库连接配置为UTC,实际插入时间会减去八小时,故转换为
2024-07-14 08:49:19
(UTC时间) - 而
createTime
字段本身就是数据库自动生成的,而我的数据库本地配置本来就是东八区时间,所以生成的时间没毛病
2. 解决
- 将 yml 文件中的数据库连接配置修改为
serverTimezone=Asia/Shanghai
即可
spring:
datasource:
url: jdbc:mysql://localhost:3306/thr?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
3. 总结
- 当
serverTimezone=UTC
时,MySQL认为所有时间都是基于UTC时间,因此JVM的北京时间2024-07-14 16:49:19
在插入时转换为2024-07-14 08:49:19
(UTC时间) - 当
serverTimezone=Asia/Shanghai
时,MySQL认为所有时间都是基于北京时间,因此JVM的北京时间2024-07-15 17:01:13
在插入时保持不变
4. 补充
- 项目中后续又增加了
expireTime
字段,该字段是由前端传递过来然后插入数据库中的,发现了类似的问题 - 前端原先传来的字段格式如下:
"expireTime": "2024-11-21T16:35:10.394Z"
- 按理来说数据库中应该是:
2024-11-21 16:35:10
,可结果却增加了八小时,是2024-11-22 00:35:10
- 根据前面的经验分析:
"2024-11-21T16:35:10.394Z"
的Z
表示 UTC- 而我现在后端插入数据库的配置是
Asia/Shanghai
,所以会将16:35:10
增加八小时后再插入数据库
- 解决办法:修改前端代码传过来的格式,传递符合 ISO 8601 格式的北京时间字符串
"expireTime": "2024-10-20T15:40:00+08:00"
- 成功: