【问题标题】:How to convert java.sql.Timestamp to java.time.OffsetDateTime?如何将 java.sql.Timestamp 转换为 java.time.OffsetDateTime?
【发布时间】:2017-08-30 05:09:27
【问题描述】:

我正在处理一个 Scala 项目,我需要将 OffsetDateTime 类型映射到 SQL Timestamp 类型。在 DB 中,我想要 UTC 时间。

OffsetDateTimeTimestamp 的转换很简单(来自this question 的提示)并且可以按预期工作:

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

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

Timestamp 转换回OffsetDateTime 没有按预期工作

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

// java.time.OffsetDateTime = 2017-04-04T19:46:33.567+02:00

新创建的OffsetDateTime添加了时区,但是时间不正确(还是UTC,需要适应实际时区)。

为什么?我做错了什么?

【问题讨论】:

    标签: java scala java-8 slick


    【解决方案1】:

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

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

    因为您将valueOf 方法传递给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
    

    现在删除了.atZoneSameInstant

    scala> Timestamp.valueOf(now.toLocalDateTime).getTime == now.toInstant.toEpochMilli
    res53: Boolean = true
    

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

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

    【讨论】:

      【解决方案2】:

      java.sql.Timestamplong 值的薄包装,表示自纪元 (1970-01-01T00:00:00.000 UTC) 以来的毫秒数 - 因此 UTC 时区隐含在 java.sql.Timestamp 中。它不能存储任何时区信息,但隐含地使用 UTC,只要每个人都知道,它就可以工作。无法将时区信息存储在 java.sql.Timestamp 中。如果您需要记住您在输入数据中收到的时区,请将其另存为数据库中的单独列。您可以在java.sql.Timestamp 中保存正确的时间 - 但不能在输入数据中保存时区。为此,您需要一个额外的字段。

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

      【讨论】:

      • 这是一个很好的答案,但您认为我们应该直接致电Timestamp.toInstant() 吗?您上面的代码调用:Instant.ofEpochMilli(tstamp.getTime)。据我了解,我们会失去毫秒以下的精度。
      猜你喜欢
      • 2013-01-27
      • 2012-09-12
      • 1970-01-01
      • 2012-05-24
      • 2012-06-27
      • 2018-12-24
      • 2012-02-18
      • 1970-01-01
      相关资源
      最近更新 更多