mybatis处理mysql的timestamp字段问题

1、大概问题:

用了阿里云的rds实例,默认是CST,平常没有问题,有一次出故障了,比预想的差了13小时,认真分析了下,原因找到了:名为 CST 的时区其实是一个很混乱的时区,在与 MySQL 协商会话时区时,Java可能会误以为是CST -0500,而不是CST +0800。

2、问题解决:

问题很好解决,去mysql上设置下:

set global time_zone = '+08:00';

set time_zone = '+08:00';

然后去修改下my.cnf配置文件,在 [mysqld] 节下增加 default-time-zone = '+08:00'。

然后让业务应用去重启下,去掉旧的连接,如果应用不方便重启,那就只有我们重启数据库了。

3、问题分析整理

名为 CST 的时区是一个很混乱的时区,有四种含义:

美国中部时间 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

比如今天是“4月3日”。为什么提到日期?因为2021年,美国从“3月14日”开始实行夏令时,美国中部时间改为 UTC-05:00,与 UTC+08:00 相差 13 小时。

public void configureTimezone() {
    String configuredTimeZoneOnServer = getServerVariable("time_zone");

    if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
        configuredTimeZoneOnServer = getServerVariable("system_time_zone");
    }

    String canonicalTimezone = getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_serverTimezone).getValue();

    if (configuredTimeZoneOnServer != null) {
        // user can override this with driver properties, so don't detect if that's the case
        if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) {
            try {
                canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor());
            } catch (IllegalArgumentException iae) {
                throw ExceptionFactory.createException(WrongArgumentException.class, iae.getMessage(), getExceptionInterceptor());
            }
        }
    }

    if (canonicalTimezone != null && canonicalTimezone.length() > 0) {
        this.serverTimezoneTZ = TimeZone.getTimeZone(canonicalTimezone);

        // The Calendar class has the behavior of mapping unknown timezones to 'GMT' instead of throwing an exception, so we must check for this...
        if (!canonicalTimezone.equalsIgnoreCase("GMT")
            && this.serverTimezoneTZ.getID().equals("GMT")) {
            throw ...
        }
    }

    this.defaultTimeZone = this.serverTimezoneTZ;
}

从上面可以看到,当 MySQL 的 time_zone 值为 SYSTEM 时,会取 system_time_zone 值作为协调时区。看下mysql里面的time参数:

mysql> show variables like '%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | CST    |
| time_zone        | SYSTEM |
+------------------+--------+
2 rows in set (0.00 sec)

因为是CST,没有明确到细节,所以可能获得是-05:00,所以TimeZone.getTimeZone(canonicalTimezone) 会给出错误的时区信息。数据库默认时区是 Asia/Shanghai +0800,误认为服务器时区为 CST -0500,实际上服务器是 CST +0800。思考:如果 Timestamp 是以 long 表示的时间戳传输,基本就不会出现问题了。

所以情况大概是如下:
1、JDBC 误认为会话时区在 CST-5

2、JBDC 把 Timestamp+0 转为 CST-5 的 String-5

3、MySQL 认为会话时区在 CST+8,将 String-5 转为 Timestamp-13

最终结果相差 13 个小时!如果处在冬令时还会相差 14 个小时!

文章参考整理来自网络,作者字母数字:https://juejin.cn/post/6844903476225376264

猜你喜欢

转载自blog.csdn.net/csdnhsh/article/details/115414275