java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java MySQL时区不一致

Java与MySQL导致的时间不一致问题分析

作者:码畜c

在使用MySQL的过程中,你可能会遇到时区相关问题,本文主要介绍了Java与MySQL导致的时间不一致问题分析,具有一定的参考价值,感兴趣的可以了解一下

时间戳与时区的关系

时间戳一般指的是Unix 时间戳:是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。

那么和时区又有什么关系呢?

public static void main(String[] args) throws ParseException {
    TimeZone bjTimeZone = TimeZone.getTimeZone("Asia/Shanghai");
    TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
    
    // 时间戳在不同时区下的日期
    Date date = new Date(0L);
    System.out.println("时间戳 0 对应的系统时间:" + date);
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sdf.setTimeZone(bjTimeZone);
    System.out.println("时间戳 0 在东八时区下表达的时间:" + sdf.format(date));
    sdf.setTimeZone(utcTimeZone);
    System.out.println("时间戳 0 在UTC时区下表达的时间:" + sdf.format(date));
    
    // 日期在不同时区下的时间戳
    sdf.setTimeZone(bjTimeZone);
    System.out.println("2024-02-25 00:00:00 在东八时区下的时间戳:" + sdf.parse("2024-02-25 00:00:00").getTime());
    sdf.setTimeZone(utcTimeZone);
    System.out.println("2024-02-25 00:00:00 在UTC时区下的时间戳:" + sdf.parse("2024-02-25 00:00:00").getTime());
}

时间戳 0 对应的系统时间:Thu Jan 01 08:00:00 CST 1970
时间戳 0 在东八时区下表达的时间:1970-01-01 08:00:00
时间戳 0 在UTC时区下表达的时间:1970-01-01 00:00:00
2024-02-25 00:00:00 在东八时区下的时间戳:1708790400
2024-02-25 00:00:00 在UTC时区下的时间戳:1708819200

查询、修改 Java 程序使用的时区

public static void main(String[] args) {
 	// 查看默认时区与时区ID
 	TimeZone defaultTimeZone = TimeZone.getDefault();
    ZoneId systemDefaultZoneId = ZoneId.systemDefault();
	// sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null]
    System.out.println(defaultTimeZone);
    // Asia/Shanghai
    System.out.println(systemDefaultZoneId);
    
    // 设置默认时区
    TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("UTC")));
    // sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
    System.out.println(TimeZone.getDefault());
}

查询、修改 MySQL 数据库使用的时区

查询:

show global variables like "%time_zone%";
Variable_nameValue
system_time_zone(系统时区)UTC
time_zone(会话时区)SYSTEM

系统时区:UTC,即比东八时区慢8个小时。可以通过 SELECT NOW() 查询当前时间对比 PC 上的时间验证:MYSQL: 2024-02-24 19:07:31 / PC: 2024-02-25 03:07:31。该值读取的就是 MySQL 服务所在的操作系统上使用的时区,以 Linux 系统为例,可通过 date -R 查看:Sat, 24 Feb 2024 19:31:56 +0000。

会话时区:采用系统时区,即 UTC。

修改:系统时区:修改系统时区,即修改 MySQL 服务所在服务器的时区,以 Linux 操作系统为例:cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime,将时区文件 copy 到 etc 目录下且命名为 localtime。

会话时区:

JDBC 读取并设置 MySQL 服务使用的时区的流程

mysql-connector-j-8.0.33.jar 为例:

com.mysql.cj.protocol.a.NativeProtocol.configureTimeZone 设置MySQL服务使用的时区的流程:

NativeProtocol.configureTimeZone

JDBC 如何应用的时区

说明:当前Java 程序东八时区,MySQL服务 UTC 时区。

存储日期数据时,com.mysql.cj.protocol.a.SqlTimestampValueEncoder.getString 对于 Date 类型字段值的处理:将Java程序时区下的日期的时间戳,转为MySQL服务时区下的日期(2024-02-25 11:52:56 > 2024-02-25 03:52:56

com.mysql.cj.protocol.a.SqlTimestampValueEncoder.getString

查询日期数据时,com.mysql.cj.result.SqlTimestampValueFactory.localCreateFromDatetime 对于 Date 类型字段的处理:将MySQL服务时区下的日期的时间戳,转为Java程序时区下的日期。(2024-02-24 21:34:55 > 2024-02-25 05:34:55

com.mysql.cj.result.SqlTimestampValueFactory.localCreateFromDatetime

根据源代码的实现可以发现一个规律:都是先将日期根据所属时区转换为时间戳后,在根据需要转换的时区转换为最终日期。

Java 程序时区与 MySQL 服务使用时区不一致导致的问题

在 JDBC 读取并设置 MySQL 服务使用的时区的流程 中说到:MySQL 服务使用的时区会受到 jdbc 参数的影响,也就是说可能会出现:实际的数据库时区与 jdbc 参数声明的时区是不一样的。最坏情况下会出现:Java 程序时区、jdbc 声明时区、实际数据库时区都是不同的。

不对每种情况的流程进行逐个分析。一般开发时遇到时间不一致时,大概都是分为以下的两种情况:

Java 程序时区 与 jdbc 参数时区一致,实际数据库时区不一致:这种情况下,会出现程序、数据库中展示日期都是相同的,但两个日期分别对应的时间戳不相同。看起来是没有问题,但实际上,数据库中存储的日期的时间戳已经不是Java程序中该日期所对应的时间戳了。如最开始说到的:一个日期在不同时区下的时间戳是不同的,那么表达的意义也不一样,就如北京八点与美国八点的区别。整理一下 JDBC 驱动包在这种情况下的处理流程:

1)存储:

2)读取(能够在转为 Java 程序中的正确日期):

Java 程序时区 与 实际数据库时区不一致:这种情况下不考虑 jdbc 参数时区的影响,会出现程序、数据库中展示的日期不同,但两个不同日期的时间戳是一致的。捋一下 JDBC 处理流程:

1)存储:

2)读取(能够在转为 Java 程序中的正确日期):

仅依靠数据库时区生成时间数据(这是一种特殊情况):
当我们在为创建、修改时间字段添加了以下自动获取时间属性时:

`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'

1)存储:仅依赖数据库时区,不涉及时区转换的问题
2)读取(能够在转为 Java 程序中的正确日期):

解决方式

到此这篇关于Java与MySQL导致的时间不一致问题分析的文章就介绍到这了,更多相关Java MySQL时区不一致 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文