如何将java.sql.Timestamp转换为java.time.OffsetDateTime?

boh*_*717 8 java scala java-8 slick

我正在研究Scala项目,我需要将OffsetDateTime类型映射到SQL Timestamp类型.在DB中我想要UTC时间.

从转换OffsetDateTimeTimestamp非常简单(提示来自这个问题),它按预期工作:

import java.time._
import java.sql.Timestamp
val ofsdatetime = OffsetDateTime.now()
// ofsdatetime: java.time.OffsetDateTime = 2017-04-04T21:46:33.567+02:00

val tstamp = Timestamp.valueOf(ofsdatetime.atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime())
// tstamp: java.sql.Timestamp = 2017-04-04 19:46:33.567
Run Code Online (Sandbox Code Playgroud)

如您所见,时区被删除,时间戳是两个小时的时间(UTC),太棒了!

转换回TimestampOffsetDateTime 不能按预期工作:

OffsetDateTime.ofInstant(Instant.ofEpochMilli(tstamp.getTime), ZoneId.systemDefault())

// java.time.OffsetDateTime = 2017-04-04T19:46:33.567+02:00
Run Code Online (Sandbox Code Playgroud)

时区已添加到新创建的OffsetDateTime,但时间不正确(它仍然是UTC,我需要它适应实际时区).

为什么?我究竟做错了什么?

Alv*_*sco 12

虽然java.sql.Timestamp存储了epoch millis,但该.toString方法使用默认时区来呈现字符串.此外,.valueOf解释LocalDateTime使用您的默认时区.

两者的结合,导致第一次转换"看起来"正确,但实际上是错误的.值"2017-04-04 19:46:33.567"显示在您的默认TZ中,而不是UTC.

因为您传递了valueOf方法a LocalDateTime(UTC),但它将其解释为LocalDateTime(您的默认TZ).

这证明第一次转换是错误的:

scala> val now = OffsetDateTime.now
now: java.time.OffsetDateTime = 2017-04-04T14:50:12.534-06:00

scala> Timestamp.valueOf(now.atZoneSameInstant(ZoneId.of("UTC")).toLocalDateTime).getTime == now.toInstant.toEpochMilli
res54: Boolean = false
Run Code Online (Sandbox Code Playgroud)

现在.atZoneSameInstant删除:

scala> Timestamp.valueOf(now.toLocalDateTime).getTime == now.toInstant.toEpochMilli
res53: Boolean = true
Run Code Online (Sandbox Code Playgroud)

引用的stackoverflow问题的接受答案是错误的.

一旦您修复了第一次转换(删除.atZoneSameInstant),那么您的第二次转换应该可以正常工作.


rad*_*scu 6

java.sql.Timestamp是一个薄的包装器,它long表示自epoch(1970-01-01T00:00:00.000 UTC)以来的毫秒数- 因此UTC时区是隐含的java.sql.Timestamp.它不能存储任何时区信息,但隐含地它是在UTC中,并且只要每个人都知道,它一切正常.无法在a中存储时区信息java.sql.Timestamp.如果您需要记住输入数据中收到的时区,请将其另存为数据库中的单独列.您可以保存正确的时刻java.sql.Timestamp- 但不能保存输入数据中收到的时区.为此你需要一个额外的领域.

由于您希望数据库日期为UTC,因此您可以从DB中检索数据:OffsetDateTime.ofInstant(Instant.ofEpochMilli(tstamp.getTime), ZoneId.of("UTC")).这将是正确的时间点,但在UTC时区.你无法从数据库中检索的事实,OffsetDateTime是在+0200时区您将它保存到数据库之前,因为java.sql.Timestamp不存储时区的组成部分.如果您需要该信息,则需要将其存储在数据库的单独列中.