mysql时间戳14小时_SpringBoot时间戳与MySql数据库记录相差14小时排错

的小编总结,项目中遇到存储的时间戳与真实时间相差14小时的现象,以下为解决步骤。

问题

CREATE TABLE `incident` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

`recovery_time` timestamp NULL DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4;

以上为数据库建表语句,其中created_time是插入记录时自动设置,recovery_time需要手动进行设置。

测试时发现,created_time为正确的北京时间,然而recovery_time则与设置时间相差14小时。

尝试措施

jvm时区设置

//设置jvm默认时间

System.setProperty(“user.timezone”, “UTC”);

数据库时区查询

查看数据库时区设置:

show variables like '%time_zone%';

--- 查询结果如下所示:

--- system_time_zone: CST

--- time_zone:SYSTEM

查询CST发现其指代比较混乱,有四种含义(参考网址:https://juejin.im/post/5902e087da2f60005df05c3d):

美国中部时间 Central Standard Time (USA) UTC-06:00

澳大利亚中部时间 Central Standard Time (Australia) UTC+09:30

中国标准时 China Standard Time UTC+08:00

古巴标准时 Cuba Standard Time UTC-04:00

此处发现如果按照美国中部时间进行推算,相差14小时,与Bug吻合。

验证过程

MyBatis转换

代码中,时间戳使用Instant进行存储,因此跟踪package org.apache.ibatis.type下的InstantTypeHandler.

@UsesJava8

public class InstantTypeHandler extends BaseTypeHandler {

@Override

public void setNonNullParameter(PreparedStatement ps, int i, Instant parameter, JdbcType jdbcType) throws SQLException {

ps.setTimestamp(i, Timestamp.from(parameter));

}

//…代码shenglve

}

调试时发现parameter为正确的UTC时。

函数中调用Timestamp.from将Instant转换为Timestamp实例,检查无误。

/**

* Sets the designated parameter to the given java.sql.Timestamp value.

* The driver

* converts this to an SQL TIMESTAMP value when it sends it to the

* database.

*

* @param parameterIndex the first parameter is 1, the second is 2, …

* @param x the parameter value

* @exception SQLException if parameterIndex does not correspond to a parameter

* marker in the SQL statement; if a database access error occurs or

* this method is called on a closed PreparedStatement */

void setTimestamp(int parameterIndex, java.sql.Timestamp x)

throws SQLException;

继续跟踪setTimestamp接口,其具体解释见代码注释。

Sql Driver转换

项目使用com.mysql.cj.jdbc驱动,跟踪其setTimestamp在ClientPreparedStatement类下的具体实现(PreparedStatementWrapper类下实现未进入)。

@Override

public void setTimestamp(int parameterIndex, Timestamp x) throws java.sql.SQLException {

synchronized (checkClosed()。getConnectionMutex()) {

((PreparedQuery>) this.query)。getQueryBindings()。setTimestamp(getCoreParameterIndex(parameterIndex), x);

}

}

继续跟踪上端代码中的getQueryBindings()。setTimestamp()实现(com.mysql.cj.ClientPreparedQueryBindings)。

4a8acbb26818dbde333d1085c9bc2233.png

注意此处时区转换,会调用如下语句获取默认时区:

this.session.getServerSession()。getDefaultTimeZone()

获取TimeZone数据,

检查TimeZone类中offset含义,具体如下所示:

/**

* Gets the time zone offset, for current date, modified in case of

* daylight savings. This is the offset to add to UTC to get local time.

*

* This method returns a historically correct offset if an

* underlying TimeZone implementation subclass

* supports historical Daylight Saving Time schedule and GMT

* offset changes.

*

* @param era the era of the given date.

* @param year the year in the given date.

* @param month the month in the given date.

* Month is 0-based. e.g., 0 for January.

* @param day the day-in-month of the given date.

* @param dayOfWeek the day-of-week of the given date.

* @param milliseconds the milliseconds in day in standard

* local time.

*

* @return the offset in milliseconds to add to GMT to get local time.

*

* @see Calendar#ZONE_OFFSET

* @see Calendar#DST_OFFSET

*/

public abstract int getOffset(int era, int year, int month, int day,

int dayOfWeek, int milliseconds);

offset表示本地时间与UTC时的时间间隔(ms)。

计算数值offset,发现其表示美国中部时间,即UTC-06:00.

Driver推断Session时区为UTC-6;

Driver将Timestamp转换为UTC-6的String;

MySql认为Session时区在UTC+8,将String转换为UTC+8.

因此,最终结果相差14小时,bug源头找到。

解决方案

mysql> set global time_zone = '+08:00';

Query OK, 0 rows affected (0.00 sec)

mysql> set time_zone = '+08:00';

Query OK, 0 rows affected (0.00 sec)

告知运维设置时区,重启MySql服务,问题解决。

此外,作为防御措施,可以在jdbc url中设置时区(如此设置可以不用修改MySql配置):

jdbc:mysql://localhost:3306/table_name?useTimezone=true&serverTimezone=GMT%2B8

此时,就告知连接进行时区转换,并且时区为UTC+8.

以上就是给大家做的内容详解,更多关于IT的学习,请继续关注

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值