【问题标题】:How to ignore DST adjustments made by java while retrieving date column from an Oracle database?如何在从 Oracle 数据库中检索日期列时忽略 java 所做的 DST 调整?
【发布时间】:2019-05-31 09:42:43
【问题描述】:

我有一个包含 Date 列的 Oracle 表。

+--------+-----------------------+
|  ID    | CURR_DATE             |       
+--------+-----------------------+                       
|   1    |2003-04-06 02:59:59    |
+--------+-----------------------+  

当我做一个

select * from tablename

在数据库控制台中,我得到了正确的 CURR_DATE 列值。

但是,当我在使用 JDBC 的 java 程序中执行相同操作时,由于夏令时,时间会提前一小时。这是一个解释它的链接。 https://www.timeanddate.com/time/change/usa/new-york?year=2003

所以我收到的值是 2003-04-06 03:59:59 而不是 2003-04-06 02:59:59

发生这种情况是因为 DB 和 JVM 在 PST 中,而 PST 遵循 DST。(如果我错了,请纠正我)

这是我的代码

String query="select * from tablename";         
PreparedStatement psmt = con.prepareStatement(query);
ResultSet results=psmt.executeQuery();
while(results.next()) {
    System.out.println(results.getString(2));
}

可以做些什么让results.getString()返回我与数据库中相同的值(不做任何调整)。

我希望它返回原始值 2003-04-06 02:59:59 而不是调整后的值 2003-04-06 03:59:59 >.

【问题讨论】:

  • 为什么不在 UTC 上工作并在您的代码中管理 DST?
  • @HimanshuBhardwaj 更多解释可能会有所帮助。为什么不帮忙回答?我对这些时区术语不太了解。
  • 或许可以试试results.getObject(2, LocalDateTime.class)
  • @HimanshuBhardwaj UTC 或 DST 都不重要,因为 Oracle DATE 类型仅包含日期和时间,没有任何时区概念或与 UTC 的偏移量。

标签: java oracle datetime jdbc timezone


【解决方案1】:

正如@gord-thompson 解释的那样,您应该使用最新的 ojdbc8 或 ojdbc10,最好是 19.3 或 18.3,即使您的数据库是 11.2g https://www.oracle.com/technetwork/database/enterprise-edition/jdbc-faq-090281.html#01_02

,您也可以使用它们
results.getObject(2, LocalDateTime.class)

说明

问题在于java.sql.Timestamp 类的实现。它表示自 1970-01-01 00:00:00 GMT 和 2003-04-06 02:59:59 在 JVM 时区、2003-04-06 02:59:59 以来的毫秒数JVM 时区不存在,因此解析器因 DST 更改而关闭。

LocalDateTime 不受任何时区约束,只是年-月-日-时-分-秒-nano,因此无论 JVM 时区如何,代表 2003-04-06 02:59:59 都没有问题。

【讨论】:

  • 使用 ojdbc8 尝试了您和 Gord 的方法。得到以下异常 - 方法 oracle/jdbc/driver/OracleResultSetImpl.getObject(ILjava/lang/Class;)Ljava/lang/Object;是抽象的
  • 你没有使用 ojdbc8 或 ojdbc10
【解决方案2】:

tl;博士

java.time.LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;

使用智能对象,而不是哑字符串

您将日期时间值存储在日期时间列中,因此请在 Java 中使用日期时间类型。目前您正在使用字符串。

Oracle DATE 类型仅包含日期和时间,没有任何时区概念或与 UTC 的偏移量。因此,DATE 列不能用于表示时刻。标准 SQL 中的等价物是 TIMESTAMP WITHOUT TIME ZONE

例如,如果某行在该列中存储了今年 1 月 23 日中午的值,我们就有办法知道这是东京的中午、加尔各答的中午、巴黎的中午还是印度的中午蒙特利尔。这些不同的中午都发生在不同的时刻,相隔数小时。

在 Java 中匹配 Oracle DATE 列的适当类型是 LocalDateTime。此类也缺少任何时区或与 UTC 偏移的概念。所以它不能用来代表一个时刻。但是,当您只有一个没有区域/偏移量的日期和时间时,这是适合您的课程。

JDBC 4.2 及更高版本需要支持直接与数据库交换 java.time 对象。

LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;

写入数据。

myPreparedStatemen.setObject( … , ldt ) ;

通过将没有任何区域/偏移量的 Java 对象与没有任何区域/偏移量的类型的数据库列交换,您不会遇到区域/偏移量问题。但请注意这种日期时间值的局限性:

  • 不是片刻,也不是时间线上的一个点。
  • 含糊不清,因为它可以解释为大约 26-27 小时(全球时区范围)内的许多潜在时刻。

【讨论】:

  • 该图是错误的,JDBC 将TIMESTAMP WITHOUT TIME ZONE 映射到java.sql.Timestampjava.time.LocalDateTime,而TIMESTAMP WITH TIME ZONE 没有对应的遗留类型。并且 JDBC 不提供对 InstantZonedDateTime 的支持。
  • 我相信表格说明了其中的一部分:'Instant' 和 ZonedDateTime 不受 JDBC 支持,因为缺少紫色的“JDBC 4.2”栏。
猜你喜欢
  • 1970-01-01
  • 2013-07-25
  • 2016-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多