【问题标题】:How to directly retrieve a UTC OffsetDateTime using MySQL Connector/J?如何使用 MySQL Connector/J 直接检索 UTC OffsetDateTime?
【发布时间】:2022-01-14 22:44:57
【问题描述】:

我更喜欢所有时间都使用 UTC,除了显示时间。在最后一刻,它们可以转换为本地时间,仅用于显示。

我之前一直在 MySQL 中将 UNIX 时间戳(自纪元以来的秒数)存储在一个整数字段中。我正在使用其他人的数据库架构,我无法更改它使用 DATETIME 存储原始时间甚至没有偏移量。

如何在 UTC 中使用数据库检索和发送时间对象,这样数据库绝对不会进行任何转换?


虽然是为 PostgreSQL 编写的,但我尝试遵循 Basil Bourque 对以下问题的出色回答。我在任何地方都使用Instant,除非他表明我在与 JDBC 交互时使用OffsetDateTime,因为它是唯一保证受 JDBC 4.2 支持的现代 moment 类。

我正在使用 MySQL Connector/J 驱动程序版本 8.0.27,并且我正在使用 &preserveInstants=false&connectionTimeZone=UTC 连接。

让我们看一下数据库中存储为“2022-01-14 11:00:00”的DATETIME,它被理解为UTC。

resultSet.getString(columnName); // "2022-01-14 11:00:00"
resultSet.getObject(columnName, OffsetDateTime.class).toString() // "2022-01-14T11:00-04:00"
resultSet.getObject(columnName, OffsetDateTime.class).toInstant().toString() // "2022-01-14T15:00:00Z"

当它创建OffsetDateTime 时,它分配了“-04:00”的系统或服务器(同一台机器)偏移量并将一切搞砸了。我认为我的&preserveInstants=false&connectionTimeZone=UTC 连接选项应该已经停止了。

我知道我可以立即使用.withOffsetSameLocal(ZoneOffset.UTC).toInstant() 将偏移量设置为“+00:00”,但我如何避免这样做并让它直接创建一个采用 UTC 的OffsetDateTime

【问题讨论】:

    标签: java mysql datetime jdbc offsetdatetime


    【解决方案1】:

    TIMESTAMP WITHOUT TIME ZONE

    您已经确定了核心问题:您的表的作者将该列定义为 MySQL DATETIME 类型。该类型仅表示具有时间的日期,但缺少时区或偏移量的上下文。 MySQL DATETIME 列类似于 SQL 标准类型 TIMESTAMP WITHOUT TIME ZONE

    java.time.LocalDateTime

    因此,您在从 DATETIME 列中检索值时使用了错误的 Java 类。不要使用OffsetDateTime,而是使用LocalDateTime。与DATETIME(和TIMESTAMP WITHOUT TIME ZONE)一样,Java 类型LocalDateTime 表示具有时间但没有区域或偏移量的日期。

    LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
    

    确定片刻

    您可以分配偏移量或时区来精确定位时间轴上的一个点。

    OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ; 
    

    或者:

    ZoneId z = ZoneId.of( "America/Edmonton" ) ;
    ZonedDateTime zdt = ldt.atZone( z ) ;
    

    你说:

    它分配“-04:00”的系统或服务器(同一台机器)偏移量并搞砸一切。

    沿着遍历路线的某些部分试图帮助您实现代码的意图,即从仅日期和时间的值开始,并以分配的偏移量结束。很可能您的 JDBC 驱动程序是分配偏移量的部分。由于 MySQL DATETIME 没有存储偏移量,因此需要分配 一些 偏移量以生成您请求的 OffsetDateTime 对象。

    如上所示,更好的方法是在使用缺少偏移量的数据库列时不要求OffsetDateTime对象。

    你问:

    如何在 UTC 中使用数据库检索和发送时间对象,这样数据库绝对不会进行任何转换?

    您不能在 UTC 中看到的时刻与缺少从 UTC 偏移的概念的数据库列交换。

    如果要在 SQL 数据库中表示时刻,则必须使用适当的数据类型,类似于 SQL 标准类型TIMESTAMP WITH TIME ZONE

    您在将带有偏移量的日期时间值与仅日期时间的数据库列交换时所要求的就像要求将日元价格存储在单纯的数字列中一样。您可以假装这样记录的值代表日元价格,但您不知道。有人也可以在那里以欧元或比索存储价格。

    由于您似乎对当前的工作情况感到困惑,您可以假装您的TIMESTAMP WITHOUT TIME ZONE 列存储UTC 值。本质上,这就是我上面的代码示例所做的。

    在写入值时假装,调整为 UTC,然后提取 LocalDateTime

    ZonedDateTime zdt = ZonedDateTime.now( z ) ;
    OffsetDateTime odt = zdt.withOffsetSameInstant( ZoneOffset.UTC ) ; 
    LocalDateTime ldt = odt.toLocalDateTime() ;
    myPreparedStatement.setObject( … , ldt ) ;
    

    这种伪装的做法充满危险,而且不负责任。该数据库正在跟踪虚构,而不是真相。请务必记录此 hack,因为任何刚接触该系统的程序员都会完全糊涂。

    顺便说一句,请注意类型名称 TIMESTAMP WITH TIME ZONETIMESTAMP WITHOUT TIME ZONE 用词不当。根据我被告知的情况以及我在草稿中看到的情况,SQL 标准实际上只意味着偏移量,而不是真正的时区。这就是为什么 OffsetDateTime 是 JDBC 中唯一映射到与 TIMESTAMP WITH TIME ZONE 匹配的 Java 类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-20
      • 2014-09-22
      • 2023-03-30
      • 2018-06-24
      • 2020-02-26
      • 2013-12-28
      • 2019-12-01
      相关资源
      最近更新 更多