【问题标题】:MySql Timezone JDBC issueMySql 时区 JDBC 问题
【发布时间】:2019-12-27 00:52:05
【问题描述】:

我正在尝试在 MySql 表名人员和列名 regdate 中插入一个日期值,数据类型 = datetime。我正在设置一个值,例如'2019-08-21 20:25:20' 但在保存 +5:30 小时后会添加,并且存储的值是 '2019-08-22 03:55:20'。使用下面的 Java 代码生成日期值

Timestamp curDate = Timestamp.valueOf(Instant.now().atZone(ZoneId.of("Asia/Kolkata")).toLocalDateTime());

然后在 INSERT 查询中使用.setTimestamp(1, curdate);

我已检查 MySql 的时区是否设置为 IST (GMT+0530)。应用服务器时区也设置为 IST。但即使我明确设置日期值,我也无法理解为什么要添加 +5:30 小时。

我尝试将连接字符串中的时区设置为?serverTimezone=Asia/Kolkata,但没有成功。

但是,如果我使用连接相同 MySql 实例的本地计算机运行相同的代码,我不会遇到任何问题,并且相同的值会在不增加 5:30 小时的情况下被存储。我检查了 App Server 时区,它是 IST。

MySql 版本 - 5.7.17-log mysql-connector-java - 8.0.15

我错过了什么吗?

【问题讨论】:

    标签: mysql jdbc timezone


    【解决方案1】:

    这里有一些问题。

    避免使用旧的日期时间类

    首先,您将糟糕的传统日期时间类 (java.sql.Timestamp) 与现代 java.time 类混合在一起。别。仅使用 java.time 包中的类。

    LocalDateTime 不能代表片刻

    您正在使用LocalDateTime 跟踪某个时刻,但它不能。根据定义,该类代表时间线上的一个点。该类具有日期和时间,但故意缺少时区或与 UTC 的偏移量的上下文。调用toLocalDateTime 会删除有关区域/偏移的重要信息。

    工具的谎言

    您可能会对许多工具在生成表示从数据库检索的日期时间值的文本时动态应用时区的善意但不幸的行为感到困惑。您可以通过使用 Java 和 JDBC 从数据库中获取纯正的值来避免这种情况。

    TIMESTAMP WITH TIME ZONE

    您未能披露数据库中列的确切数据类型。如果您尝试跟踪某个时刻,请使用类似于 SQL 标准类型 TIMESTAMP WITH TIME ZONE 的数据类型。在 MySQL 8 中,根据this doc 显然是TIMESTAMP 类型。 (我是 Postgres 人,不是 MySQL 用户。)

    在 JDBC 4.2 及更高版本中,我们可以与数据库交换 java.time 对象。所以无需再次过度触摸java.sql.Timestamp

    不幸的是,JDBC 规范奇怪地选择不支持Instant(在 UTC) nor forZonedDateTime(a moment as seen in some particular time zone). The spec does require support for [OffsetDateTime`]2 的时刻。

    提示:学习在大多数情况下使用 UTC 工作。仅在业务逻辑需要或向用户展示时才调整到时区。

    OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;  // Capture current moment in UTC.
    

    通过准备好的语句写入数据库。

    myPreparedStatement.setObject( … , odt ) ;
    

    检索。

    OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
    

    通过某个特定地区(时区)的人们使用的挂钟时间查看那一刻。

    ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
    ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
    

    【讨论】:

    • 特别是 w.r.t. MySQL,请注意 (1) 如引用的 MySQL 文档中所述,“TIMESTAMP 的范围为 '1970-01-01 00:00:01' UTC to '2038-01-19 03 :14:07' UTC。” (2) MySQL Connector/J 关于java.time.* 仍有一些问题需要解决。