简体   繁体   English

如何从 MySQL Connector/J 直接检索 UTC OffsetDateTime

[英]How to directly retrieve a UTC OffsetDateTime from MySQL Connector/J

I prefer all times to be in UTC, except for when they are displayed.我更喜欢所有时间都在 UTC,除了它们显示的时候。 At the last moment, they can be converted to a local time for display only.在最后一刻,它们可以转换为本地时间,仅用于显示。

I've always stored unix timestamps (seconds since epoch) in MySQL before, in an integer field.我之前一直在 MySQL 中存储 unix 时间戳(自纪元以来的秒数),在 integer 字段中。 I'm working with someone else's database schema that I cannot change which uses a DATETIME that stores a raw time without even an offset.我正在使用其他人的数据库架构,我无法更改它使用DATETIME存储原始时间甚至没有偏移量。

How can I retrieve and send time objects with the database in UTC, so the database is doing absolutely no conversions whatsoever?如何使用 UTC 格式的数据库检索和发送时间对象,因此数据库绝对不进行任何转换?

Although written for PostgreSQL, I've tried to follow the excellent answers by Basil Bourque on the questions below.虽然是为 PostgreSQL 编写的,但我尝试遵循 Basil Bourque 在以下问题上的出色回答。 I use Instant everywhere, except as he shows I use an OffsetDateTime when interacting with JDBC, because it's the only modern moment class guaranteed to be supported by JDBC 4.2.我在任何地方都使用Instant ,除非他表明我在与 JDBC 交互时使用了OffsetDateTime ,因为它是 Z82269B9B71AB4A7732F6958610214 保证支持的唯一现代时刻 class。

I'm using MySql Connector/J 8.0.27, and am connecting with &preserveInstants=false&connectionTimeZone=UTC .我正在使用 MySql Connector/J 8.0.27,并且正在与&preserveInstants=false&connectionTimeZone=UTC

Let's look at a DATETIME in the database stored as "2022-01-14 11:00:00" which is understood to be 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"

When it's creating the OffsetDateTime , it's assigning either the system or server (same machine) offset of "-04:00" and messing everything up.当它创建OffsetDateTime时,它分配“-04:00”的系统或服务器(同一台机器)偏移量并将一切搞砸。 I thought my connection options of &preserveInstants=false&connectionTimeZone=UTC should have stopped that.我认为我的&preserveInstants=false&connectionTimeZone=UTC的连接选项应该已经停止了。

I know I could immediately set the offset to "+00:00" afterward with .withOffsetSameLocal(ZoneOffset.UTC).toInstant() , but how do I avoid doing that and have it directly create an OffsetDateTime that is in UTC?我知道我可以立即使用.withOffsetSameLocal(ZoneOffset.UTC).toInstant()将偏移量设置为“+00:00”,但是如何避免这样做并让它直接创建一个 UTC 格式的OffsetDateTime

TIMESTAMP WITHOUT TIME ZONE

You've pinpointed the core issue: The author of your table defined the column as a MySQL DATETIME type.您已经确定了核心问题:您的表的作者将该列定义为 MySQL DATETIME类型。 That type represents only a date with time-of-day but lacks the context of a time zone or offset.该类型仅表示具有时间的日期,但缺少时区或偏移量的上下文。 That MySQL DATETIME column is akin to the SQL standard type of TIMESTAMP WITHOUT TIME ZONE . MySQL DATETIME列类似于 SQL 标准类型的TIMESTAMP WITHOUT TIME ZONE

java.time.LocalDateTime

So you are using the wrong Java class when retrieving a value from that DATETIME column.因此,在从该DATETIME列中检索值时,您使用了错误的 Java class 。 Rather than use OffsetDateTime , use LocalDateTime .与其使用OffsetDateTime ,不如使用LocalDateTime Like DATETIME ( and TIMESTAMP WITHOUT TIME ZONE ), the Java type LocalDateTime represents a date with time-of-day but no zone or offset.DATETIME (和TIMESTAMP WITHOUT TIME ZONE )一样,Java 类型LocalDateTime表示具有时间但没有区域或偏移量的日期。

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

Determine a moment确定片刻

You can the assign an offset or time zone to pinpoint a moment, a point on the timeline.您可以分配一个偏移量或时区来精确定位一个时刻,即时间线上的一个点。

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

Or:或者:

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

You said:你说:

it's assigning either the system or server (same machine) offset of "-04:00" and messing everything up.它分配了“-04:00”的系统或服务器(同一台机器)偏移量并将一切搞砸了。

Some part along the route of traversal is trying to help you along with your code's intention of starting with a date and time only value that ends up with an offset assigned.遍历路线上的某些部分试图帮助您实现代码的意图,即从仅日期和时间的值开始,并以分配的偏移量结束。 Likely your JDBC driver is the part assigning an offset.您的 JDBC 驱动程序可能是分配偏移量的部分。 Since no offset is stored with a MySQL DATETIME , some offset needs to be assigned in order to produce the OffsetDateTime object you requested.由于没有使用 MySQL DATETIME存储偏移量,因此需要分配一些偏移量才能生成您请求的OffsetDateTime object。

As shown above, a better approach is to not ask for a OffsetDateTime object when using a database column lacking an offset.如上所示,更好的方法是在使用缺少偏移量的数据库列时不要求OffsetDateTime object。

You asked:您问:

How can I retrieve and send time objects with the database in UTC, so the database is doing absolutely no conversions whatsoever?如何使用 UTC 格式的数据库检索和发送时间对象,因此数据库绝对不进行任何转换?

You cannot exchange a moment as seen in UTC with a database column lacking the concept of an offset-from-UTC.您不能在 UTC 中看到的时刻与缺少从 UTC 偏移的概念的数据库列交换。

If you want to represent moments in a SQL database, you must use the appropriate data type, a type akin to the SQL standard type TIMESTAMP WITH TIME ZONE .如果要在 SQL 数据库中表示时刻,则必须使用适当的数据类型,类似于 SQL 标准类型TIMESTAMP WITH TIME ZONE的类型。

What you are asking for in exchanging a date-time-with-offset value with a database column that is date-time-only is like asking to store a price in Japanese Yen in a mere numeric column.您在将带有偏移量的日期时间值与仅日期时间的数据库列交换时所要求的就像要求将日元价格存储在单纯的数字列中一样。 You can pretend that such recorded values represent a price in Yen, but you do not know.您可以假装这些记录值代表日元价格,但您不知道。 Someone could just as well store prices in Euros or Pesos there.有人也可以在那里以欧元或比索存储价格。

Since you seem to be stuck in a pickle regarding your current work situation, you can pretend that your TIMESTAMP WITHOUT TIME ZONE column stores UTC values.由于您似乎对当前的工作情况感到困惑,您可以假装您的TIMESTAMP WITHOUT TIME ZONE列存储 UTC 值。 That is what my code example above does, essentially.本质上,这就是我上面的代码示例所做的。

To pretend while writing values, adjust to UTC, then extract a LocalDateTime .要在写入值时假装,请调整为 UTC,然后提取LocalDateTime

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

This pretending approach is fraught with peril, and irresponsible.这种伪装的做法充满了危险,而且是不负责任的。 The database is tracking a fiction, not the truth.该数据库正在跟踪虚构,而不是真相。 Be sure to document this hack, as any programmer new to the system would be thoroughly confused.请务必记录此 hack,因为任何刚接触该系统的程序员都会被彻底搞糊涂。

By the way, notice that the type names TIMESTAMP WITH TIME ZONE and TIMESTAMP WITHOUT TIME ZONE are misnomers.顺便说一下,请注意类型名称TIMESTAMP WITH TIME ZONETIMESTAMP WITHOUT TIME ZONE用词不当。 From what I've been told, and from what I've seen in drafts, the SQL standard actually meant offsets only, not really time zones.据我所知,以及我在草稿中看到的情况,SQL 标准实际上只意味着偏移量,而不是真正的时区。 That is why OffsetDateTime is the only Java class mapped in JDBC for matching to TIMESTAMP WITH TIME ZONE .这就是为什么OffsetDateTime是唯一的 Java class 映射到 JDBC 以匹配TIMESTAMP WITH TIME ZONE的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM