【问题标题】:Oracle/JDBC: Problem with correct Timezone of rowsOracle/JDBC:行的正确时区问题
【发布时间】:2019-03-25 20:28:03
【问题描述】:

我有一个表 TAB,每行有一个时间戳 (DT) 和一个值 (VAL)。

 DT           NOT NULL   TIMESTAMP(0) WITH LOCAL TIME ZONE
 VAL          NOT NULL   NUMBER

我尝试读取并获取 UTC 时间戳。

SELECT SYS_EXTRACT_UTC (dt) AS dtm, val FROM mytab;

我觉得 Sqlplus 中的结果没问题。

DTM                 VAL
---------------------------
30.03.19 23:00:00   124
31.03.19 00:00:00   125
31.03.19 01:00:00   126
31.03.19 02:00:00   127
31.03.19 03:00:00   128
31.03.19 04:00:00   129
31.03.19 05:00:00   130

现在我在 Java/JDBC 中做同样的事情。

java.sql.Timestamp ts = rs.getTimestamp ("dtm");
java.util.Date dt = rs.getDate ("dtm"); 
java.util.Date tm = rs.getTime ("dtm");
ZonedDateTime zdt = ts.toInstant ().atZone (ZoneOffset.UTC);

System.out.println("ts="+ts.toString() + "     val="+rs.getDouble("wert") +
     "   dt=" + dt.toString() + "    tm=" + tm.toString() +"      zdt="+zdt);

结果不是我对时区的预期。

ts=2019-03-30 23:00:00.0     val=124.0   dt=2019-03-30    tm=23:00:00      zdt=2019-03-30T22:00Z
ts=2019-03-31 00:00:00.0     val=125.0   dt=2019-03-31    tm=00:00:00      zdt=2019-03-30T23:00Z
ts=2019-03-31 01:00:00.0     val=126.0   dt=2019-03-31    tm=01:00:00      zdt=2019-03-31T00:00Z
ts=2019-03-31 03:00:00.0     val=127.0   dt=2019-03-31    tm=02:00:00      zdt=2019-03-31T01:00Z
ts=2019-03-31 03:00:00.0     val=128.0   dt=2019-03-31    tm=03:00:00      zdt=2019-03-31T01:00Z
ts=2019-03-31 04:00:00.0     val=129.0   dt=2019-03-31    tm=04:00:00      zdt=2019-03-31T02:00Z
ts=2019-03-31 05:00:00.0     val=130.0   dt=2019-03-31    tm=05:00:00      zdt=2019-03-31T03:00Z

为什么 ts 有双 3?我希望它在UTC。 唯一正确的时间是 tm。

这是为什么呢?这里有什么问题?

JRE 1.8 甲骨文 12c 具有本地时区 CET/CEST 的 Linux

【问题讨论】:

  • 我建议您切换到 Java 8(和 JDBC 4.2)中引入的 java.time.LocalTimejava.time.LocalDatejava.time.LocalDateTime

标签: java oracle jdbc


【解决方案1】:

时区 CET/CEST 在 2019 年 3 月 31 日 02:00:00 切换到夏令时,因此从 02:00:00 跳到 03:00:00。

getDate 在 2019 年 3 月 31 日午夜返回 java.sql.Date,即 java.util.Date,因此没有 DST。

getTime 在给定时间的 1970-01-01 返回 java.sql.Time,即 java.util.Date,因此没有 DST。

getTimestamp 在给定日期和时间返回 java.sql.Timestamp,即 java.util.Date,因此应用 DST,将 02:00 更改为 03:00。但是,数据库中的值没有时区,因此剩余的值不会移动。这是TIMESTAMP WITH LOCAL TIME ZONE处理中的一个缺陷。

java.sql.Timestamp 转换为java.time.Instant 会针对夏令时进行调整,因此会影响时间。

请注意,TIMESTAMP WITH LOCAL TIME ZONE 不存储时区,它只是告诉客户端 API 代表您转换为会话时区。数据库中的实际值取决于数据库时区,而不是客户端时区或会话时区。

【讨论】:

    【解决方案2】:

    来自 Java 文档: https://docs.oracle.com/javase/8/docs/api/java/sql/Timestamp.html Timestamp,一个围绕 java.util.Date 的瘦包装器,它允许 JDBC API 将其识别为 SQL TIMESTAMP 值。它增加了保存 SQL TIMESTAMP 小数秒值的能力,方法是允许将小数秒指定为纳秒精度。 Timestamp 还提供格式化和解析操作以支持时间戳值的 JDBC 转义语法。

    Timestamp 对象的精度计算为:

    • 19,即yyyy-mm-dd hh:mm:ss中的字符数
    • 20 + s ,即 yyyy-mm-dd hh:mm:ss.[fff...] 中的字符数,s 表示给定 Timestamp 的刻度,即其小数秒精度。

    格式化时间戳的方法有很多种,下面是一个这样的例子:

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Timestamp ts = Timestamp.valueOf("2019-03-30 23:00:00.0");
    System.out.println(sdf.format(ts));
    

    【讨论】:

      猜你喜欢
      • 2019-12-27
      • 1970-01-01
      • 1970-01-01
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-03
      • 1970-01-01
      • 2018-05-23
      相关资源
      最近更新 更多