在 sql server 中使用 UTC 时间保存当前日期的问题

Mop*_*mar 1 java hibernate sql-server-2014

我想UTC在 sql server 2014 中使用时区保存日期。我曾经ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("UTC"))使用 Hibernate 会话对象获取当前日期时间并将其持久化到 db。在调试时,我可以看到在 Java 程序中日期是这样的2019-09-25T13:22:29.573Z[UTC],但是在数据库列中保存后它是这样的2019-09-25 18:53:23.3630000。它会根据系统时间自动转换时间部分。任何人都可以请建议是什么问题?我datetime2在数据库中创建此列时使用了数据类型。

Bas*_*que 5

类型错误

您的列使用了错误的数据类型。

类型datetime2不能代表片刻。该类型仅存储日期和时间,但缺少时区或 UTC 偏移量的上下文。因此,如果您存储“2020 年 1 月 23 日中午”,我们无法知道您是指日本东京的中午、突尼斯突尼斯的中午还是美国俄亥俄州托莱多的中午,这三个不同的时刻相隔几个小时。这种错误类型类似于 SQL 标准类型TIMESTAMP WITHOUT TIME ZONE。Java 中的等效类是LocalDateTime.

如果您已经存储了数据,则需要重构数据库。基本上,添加一个正确类型的新列,为每一行复制数据,随时转换。当然,这仅在您知道保存的每个值中不存在的预期时区时才有效。

正确的类型

虽然我不使用 Microsoft SQL Server,但根据文档,您应该使用类型datetimeoffsetcolumn to record a moment。此类型将任何提交的值调整为 UTC 以进行查询和排序,同时还记录偏移量。此类型类似于 SQL 标准类型TIMESTAMP WITH TIME ZONE

通常最好以 UTC 格式存储您的时刻,偏移量为零时-分-秒。

Instant类表示UTC片刻。

Instant instant = Instant.now() ;
Run Code Online (Sandbox Code Playgroud)

如果您有ZonedDateTime,则可以提取Instant.

Instant instant = zdt.toInstant() ;
Run Code Online (Sandbox Code Playgroud)

我们想将其Instant直接保存到数据库中。不幸的是,JDBC 4.2 soec 不需要支持两个最常用的java.time类:InstantZonedDateTime. 该规范确实需要支持OffsetDateTime. 所以我们转换。

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
Run Code Online (Sandbox Code Playgroud)

提交到数据库。

myPreparedStatement.setObject( … , odt ) ; 
Run Code Online (Sandbox Code Playgroud)

取回。

OffsetDateTime odt = MyResultSet.getObject( … , OffsetDateTime.class ) ;
Run Code Online (Sandbox Code Playgroud)

提取 anInstant以在您的代码中明确您需要 UTC。

Instant instant = odt.toInstant() ;
Run Code Online (Sandbox Code Playgroud)

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

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Run Code Online (Sandbox Code Playgroud)

Java(旧版和现代版)和标准 SQL 中的日期时间类型表。