我有一个表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
答案 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对象的精度计算为:
有很多格式化时间戳的方法,下面是一个这样的示例:
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));