如何使用 MySQL Connector/J 直接检索 UTC OffsetDateTime?

use*_*689 3 java mysql datetime jdbc offsetdatetime

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

我之前总是将 UNIX 时间戳(自纪元以来的秒数)存储在 MySQL 中的整数字段中。我正在使用别人的数据库模式,我无法更改该模式,它使用DATETIME存储原始时间甚至没有偏移量的数据库模式。

如何使用 UTC 格式的数据库检索和发送时间对象,以便数据库完全不进行任何转换?


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

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

让我们看一下DATETIME数据库中存储为“2022-01-14 11:00:00”的 a,它被理解为 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"
Run Code Online (Sandbox Code Playgroud)

当它创建 时OffsetDateTime,它会分配系统或服务器(同一台机器)偏移量“-04:00”并搞乱一切。我认为我的连接选项&preserveInstants=false&connectionTimeZone=UTC应该可以阻止这种情况。

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

Bas*_*que 6

TIMESTAMP WITHOUT TIME ZONE

\n

您\xe2\x80\x99已经指出了核心问题:表的作者将该列定义为MySQLDATETIME类型。该类型仅表示带有时间的日期,但缺少时区或偏移量的上下文。该 MySQLDATETIME列类似于 SQL 标准类型的TIMESTAMP WITHOUT TIME ZONE.

\n

java.time.LocalDateTime

\n

因此,当从该列检索值时,您使用了错误的 Java 类DATETIME。与其使用,不如OffsetDateTime使用LocalDateTimeDATETIME与( 和)类似TIMESTAMP WITHOUT TIME ZONE,Java 类型LocalDateTime表示带有时间但没有时区或偏移量的日期。

\n
LocalDateTime ldt = myResultSet.getObject( \xe2\x80\xa6 , LocalDateTime.class ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

确定一个时刻

\n

您可以指定偏移量或时区来精确定位时间轴上的某个时刻。

\n
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ; \n
Run Code Online (Sandbox Code Playgroud)\n

或者:

\n
ZoneId z = ZoneId.of( "America/Edmonton" ) ;\nZonedDateTime zdt = ldt.atZone( z ) ;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

你说:

\n
\n

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

\n
\n

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

\n

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

\n

你问:

\n
\n

如何使用 UTC 格式的数据库检索和发送时间对象,以便数据库完全不进行任何转换?

\n
\n

您无法将 UTC 中的时刻与缺少 UTC 偏移量概念的数据库列交换。

\n

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

\n

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

\n

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

\n

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

\n
ZonedDateTime zdt = ZonedDateTime.now( z ) ;\nOffsetDateTime odt = zdt.withOffsetSameInstant( ZoneOffset.UTC ) ; \nLocalDateTime ldt = odt.toLocalDateTime() ;\nmyPreparedStatement.setObject( \xe2\x80\xa6 , ldt ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

这种假装的做法充满危险且不负责任。该数据库正在追踪虚构的内容,而不是事实。请务必记录此黑客行为,因为任何刚接触该系统的程序员都会完全感到困惑。

\n

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

\n