2017-04-04 120 views
4

我正在开发一个Scala项目,我需要将OffsetDateTime类型映射到SQL Timestamp类型。在数据库我想有UTC时间。如何将java.sql.Timestamp转换为java.time.OffsetDateTime?

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) ,太棒了!

转换回TimestampOffsetDateTime不能按预期工作

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

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

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

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

回答

9

尽管java.sql.Timestamp存储时代毫秒,.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 
.atZoneSameInstant删除

现在:

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

接受的答案被引用的计算器的问题是错误的。

一旦你修复了第一次转换(删除.atZoneSameInstant),那么你的第二次转换应该工作得很好。

2
java.sql.Timestamp

是周围从epoch(1970-01-01T00:00:00.000 UTC)一个long代表值毫秒的薄包装纸 - 这样的UTC时区是在java.sql.Timestamp隐式的。它不能存储任何时区信息,但隐含在UTC中,只要每个人都知道,它一切正常。没有办法将时区信息存储在java.sql.Timestamp中。如果您需要记住您在输入数据中收到的时区,请将其另存为数据库中的单独列。您可以在java.sql.Timestamp中保存一个正确的时间 - 但不是输入数据中收到的时区。为此,你需要一个额外的领域。

既然你喜欢你的数据库日期是UTC,你可以像这样从数据库中检索数据:OffsetDateTime.ofInstant(Instant.ofEpochMilli(tstamp.getTime), ZoneId.of("UTC"))。这将是正确的时间点,但在UTC时区。因为java.sql.Timestamp不存储时区组件,所以您无法从数据库检索到OffsetDateTime时区在+0200时区之前的事实。如果您需要这些信息,则需要将其存储在数据库的单独列中。

相关问题