Oracle / JDBC:行的正确时区存在问题

时间:2019-03-25 20:28:04

标签: java oracle jdbc

我有一个表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

2 个答案:

答案 0 :(得分:2)

时区CET / CEST在2019-03-31的02:00:00切换为夏令时,因此从02:00:00跳到03:00:00。

getDate返回一个java.sql.Date,该java.util.Date是午夜2019-03-31的getTime,因此没有夏令时。

java.sql.Time返回一个java.util.Date,它是给定时间在1970-01-01的getTimestamp,因此没有夏令时。

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

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

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

答案 1 :(得分:1)

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

Timestamp对象的精度计算为:

  • 19,即yyyy-mm-dd hh:mm:ss中的字符数
  • 20 + s,它是yyyy-mm-dd hh:mm:ss中的字符数。[fff ...]和s表示给定时间戳记的小数位数,秒精度。

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

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));