在 UTC 的 Postgres 中存储 ZonedDateTime

Storing ZonedDateTime in Postgres in UTC

提问人:Oh hi Mark 提问时间:9/28/2023 最后编辑:AnonymousOh hi Mark 更新时间:9/28/2023 访问量:101

问:

我收到一个 UTC 中的变量,我想在 UTC 中保留 Postgres 列。该列当前是 type of,我想阅读的格式相同。两者之间没有转换。ZonedDateTimetimestamptzZonedDateTime

回读它会转换为我不想要的本地时区。我的问题是,这是正确的吗?ZonedDateTimetimestamptz

Java PostgreSQL spring-data-jpa timestamp-with-timezone zoneddatetime

评论

2赞 Adrian Klaver 9/28/2023
timestamptz不存储时区或偏移量。它只是将值存储为 UTC 时间。读取日期时间:第 8.5.1.3 节。完整说明的时间戳
0赞 Oh hi Mark 9/28/2023
所以它符合我的目的,因为我收到的总是UTC。
1赞 Adrian Klaver 9/28/2023
如果您还没有阅读我提到的部分,我强烈建议您阅读。正如你所发现的,你得到的值可能不是在UTC上。
0赞 Arvind Kumar Avinash 9/28/2023
查看此答案和此答案,了解如何将 API 与 JDBC 一起使用。java.time
0赞 Anonymous 9/28/2023
ZonedDateTime对于 UTC 中的日期和时间来说,它并不是正确的类型。如果可以更改它,请选择更正确且重量更轻。OffsetDateTime

答:

1赞 Basil Bourque 9/28/2023 #1

TL的;博士

写作:

myPreparedStatement.setObject( … , myZonedDateTime.toOffsetDateTime() )

检索:

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

您无法取回对象,因为在 Postgres 和 SQL 标准中不支持将具有时区的时刻作为数据类型。ZonedDateTime

SQL 标准

SQL 标准几乎没有提及某些日期时间类型,而没有对细节和行为进行太多定义。因此,数据库在处理日期时间的方法上有很大差异。

SQL 标准定义了类型和 。但这些都是用词不当,是标准。作者的意思是与UTC的偏移量,而不是实际的时区。TIMESTAMP WITH TIME ZONETIMESTAMP WITHOUT TIME ZONE

JDBC 映射

因此,JDBC 标准将值映射到 java.time.OffsetDateTime 类型。该类型表示与UTC偏移量为一定小时-分钟-秒的时刻,正(向东)或负(向西)。TIMESTAMP WITH TIME ZONE

JDBC 既不寻址 java.time.ZonedDateTime 类,也不寻址。java.time.Instant

转换为ZonedDateTimeOffsetDateTime

我在 UTC 中收到一个变量作为 ZonedDateTime,我想在 UTC 中保留 Postgres 列。

将 ur 转换为 .ZonedDateTimeOffsetDateTime

OffsetDateTime odt = myZdt.toOffsetDateTime() ;

例如,今年分配了区域的日期时间将转换为偏移量为 或 的日期时间。Europe/Paris+01:00+02:00

在数据库中写入/检索

对象中的特定偏移量无关紧要。当保存到 类型的 Postgres 列时,任何提供的偏移量或区域信息都用于将值调整为零小时-分钟-秒的偏移量。此调整是特定于 Postgres 的行为,未在标准中指定。其他一些数据库也这样做,有些则不然。OffsetDateTimeTIMESTAMP WITH TIME ZONE

发送到数据库。

myPreparedStatement.setObject( … , odt ) ;

检索。

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

您无法直接取回您的对象。如上所述,Postgres 和 SQL 标准都没有使用时区存储日期时间。ZonedDateTime

如果原始时区对您很重要,则必须将其单独存储在另一个文本列中。

myPreparedStatement.setString( … , zdt.getZone().toString() ) ;

检索。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
ZoneId zoneId = ZoneId.of( myResultSet.getString( … ) ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( zoneId ) ;

顺便说一句,请注意,时区名称有时会发生变化。

始终 UTC

Postgres 始终将日期时间值存储在偏移量为零的列中。TIMESTAMP WITH TIME ZONE

JDBC 始终从列中检索值作为偏移量为零的对象。TIMESTAMP WITH TIME ZONEOffsetDateTime

⚠️ 不幸的是,一些工具、中间件和框架选择注入默认时区或偏移量。在数据库和您之间,检索到的 UTC 值可能会被调整。这不会发生在直接 JDBC 上。但要注意建立在JDBC之上的框架;这些可以选择注入偏移量或区域。虽然用心良苦,但这种反功能确实会引起很多混乱。

使用完整类型名称

我建议你不要使用缩写.timestampz

首先,该术语是 Postgres 专有的,而不是标准的。

另一方面,这个词太容易被误读为.鉴于正确选择 versus 的重要性,我建议始终将它们拼出来。timestampTIMESTAMP WITH TIME ZONETIMESTAMP WITHOUT TIME ZONE