在 Oracle 时间戳列中以 UTC 保存日期

Posted

技术标签:

【中文标题】在 Oracle 时间戳列中以 UTC 保存日期【英文标题】:Saving date in UTC in Oracle Timestamp column 【发布时间】:2020-01-14 19:50:54 【问题描述】:

我需要在 TIMESTAMP WITH TIMEZONE 类型的 Oracle 列中保存当前 UTC 日期和时间。这是来自带有 JPA 和 Hibernate 的 Spring Boot 服务

我在我的应用程序 yml 中启用了以下功能

  jpa:
    hibernate:
      ddl-auto: none
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.Oracle12cDialect
        jdbc:
          time_zone: UTC

Entity 类字段看起来像

@Column(name = "last_user_edit_date", columnDefinition = "TIMESTAMP WITH TIME ZONE")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private ZonedDateTime lastUserEditDate;

在设置日期时我正在使用

obj.setLastUserEditDate(ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("UTC")));

以上对于实际日期值来说工作正常。唯一的问题是在数据库中它正在保存 UTC 时间,但提到 MST(我的本地时区)作为时区。例如,保存的值是

12-SEP-19 09.50.53.820000000 PM 美国/丹佛

这里的 9.50 PM 实际上是 UTC 时间,但时区是 AMERICA/DENVER。我想要的是

12-SEP-19 09.50.53.820000000 PM UTC

我怎样才能实现这个 Spring JPA 和 Java 8 类?

谢谢

【问题讨论】:

【参考方案1】:

LocalDateTime 是错误的类

LocalDateTime 类无法及时跟踪。我无法想象调用LocalDateTime.now() 有意义的场景。在使用类之前阅读 Javadoc。

跟踪片刻:InstantOffsetDateTimeZonedDateTime

要跟踪时刻,请使用 Instant(始终采用 UTC)、OffsetDateTime(与 UTC 有偏移的时刻)或 ZonedDateTime(在特定区域看到的时刻)。

奇怪的是,JDBC 4.2 需要支持 OffsetDateTime,但保留了最常见的两个类,InstantZonedDateTime,可选。

因此,要捕获 UTC 中的当前时刻以进行 JDBC 工作:

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

或者更长的:

Instant instant = instant.now() ;  // Capture current moment in UTC.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

发送到数据库:

myPreparedStatement.setObject( … , odt ) ;

从数据库中检索:

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

JPA

我不使用JPA。但看起来这个问题已经涵盖了,JPA Storing OffsetDateTime with ZoneOffset。并看到这篇文章,What’s new in JPA 2.2 – Java 8 Date and Time Types

其他时区和时差

唯一的问题是在数据库中它正在保存 UTC 时间但提到了 MST

This documentation for Oracle Database 似乎说 TIMESTAMP WITH TIME ZONE 类型确实记录了传入数据的时区或与 UTC 的偏移量。其他一些数据库,如 Postgres 将传入的值调整为 UTC(零时分秒的偏移量)。

要获取 UTC,如上所示检索 OffsetDateTime,并调用 toInstant 方法以生成始终采用 UTC 的 Instant 对象。或者生成另一个OffsetDateTime,肯定是UTC:

OffsetDateTime odtUtc = odt.withOffsetSameInstant​( ZoneOffset.UTC ) ;

【讨论】:

以上是关于在 Oracle 时间戳列中以 UTC 保存日期的主要内容,如果未能解决你的问题,请参考以下文章

将 UTC 时间戳(毫秒)插入 Oracle 时间戳列

从返回的 Oracle 时间戳列中检索数据

关于在SQL Server中使用UTC时间保存当前日期的问题

如何从 H2 中的时间戳列中提取日期

从纪元时间戳列中仅检索具有今天日期的记录

SQL (RedShift):从时间戳列中为每个月选择不同的日期