【问题标题】:How to treat all dates as UTC if Oracle's dbTimezone is different?如果 Oracle 的 dbTimezone 不同,如何将所有日期视为 UTC?
【发布时间】:2020-05-20 17:56:42
【问题描述】:

我有一个 Oracle 数据库,它提供+00:00 查询结果:

从对偶中选择 dbTimezone;

我想在我的 Java 应用程序中使用任何日期/时间始终为 UTC。

我使用的表有一个Date 类型的列,它从sysdate 获取默认值。

即使我使用-Duser.timezone=UTC 运行我的应用程序,但当我插入此表并使用rs.getTimestamp("DATE_CREATED") 读取此Date 列时,它比预期的当前UTC 时间多一小时:

Expected current UTC: 2020-02-04 20:38:12.0
Actual: 2020-02-04 21:38:12.0

我还注意到 select current_date from dual 而不是 sysdate 将导致预期的当前 UTC 时间。我应该将此列的默认值从 sysdate 更改为 current_date (alter table MY_TABLE modify DATE_CREATED default current_date) 还是有其他方法可以在 Java 应用程序中配置时区而不更改数据库设置?

【问题讨论】:

  • 我强烈建议您使用 java.time 而不是 java.sql.Timestamp。调用 getObject("DATE_CREATED", java.sql.LocalDateTime.class) 读取 TIMESTAMP 值。请注意,根据 SQL 规范,TIMESTAMP 值没有时区。
  • @DouglasSurber 谢谢!与您的建议相比,resultSet.getTimestamp().toLocalDateTime() 有什么缺点吗?
  • @WernfriedDomscheit 这非常有用。现在我明白为什么sysdate 显示错误的 GMT+0 时间(它应该与 UTC 匹配,但在我的示例中不匹配)。 dbTimezonesysdate 无关! sysdate 依赖于 db 的服务器操作系统
  • @Kirill,抱歉回复晚了。 resultSet.getTimestamp(n).toLocalDateTime() 进行两次转换。 resultSet.getObject(n, LocalDateTime.class) 只做一个。此外,使用 getTimestamp 时会出现涉及系统时区的复杂情况,而使用 getObject(n, LocalDateTime.class) 时可以避免这些复杂情况。后者具有更好、更清晰的规范。

标签: java oracle jdbc timezone ojdbc


【解决方案1】:

你可以使用:

CREATE TABLE mytable (
  id NUMBER PRIMARY KEY,
  date_created DATE
);

ALTER TABLE mytable MODIFY date_created DEFAULT SYSTIMESTAMP AT TIME ZONE 'UTC';

(系统时间戳将转换为UTC时区,然后隐式转换为DATE数据类型作为默认值。)

然后:

INSERT INTO mytable( id ) VALUES ( 1 );

SELECT * FROM mytable;

输出:

身份证 |创建日期 -: | :----------------- 1 | 2020-02-04 21:57:47

db小提琴here

【讨论】:

    【解决方案2】:

    您可以从 Date 获取时间(以毫秒为单位)并转换为您想要的格式:

    OffsetDateTime now = OffsetDateTime.now(ZoneOffset.of("+02:00"));
    System.out.println("+02:00: " + now); //+02:00: 2020-02-04T22:59:11.617457100+02:00
    System.out.println("+02:00 in millis: " + now.toInstant().toEpochMilli()); //+02:00 in millis: 1580849951617
    
    OffsetDateTime utc = Instant.ofEpochMilli(now.toInstant().toEpochMilli()).atOffset(ZoneOffset.UTC); 
    System.out.println("UTC: " + utc); //UTC: 2020-02-04T20:59:11.617Z
    System.out.println("UTC in millis: " + utc.toInstant().toEpochMilli()); //UTC in millis: 1580849951617
    

    以毫秒为单位的时间应该始终相同。

    【讨论】:

    • 对于utc,更简单的转换是now.withOffsetSameInstant(ZoneOffset.UTC)。它还将保留微秒(我觉得这很好,即使它们在自纪元以来的后续转换为毫秒时显然会丢失)。
    猜你喜欢
    • 2019-09-07
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    相关资源
    最近更新 更多