代码之家  ›  专栏  ›  技术社区  ›  chris01

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

  •  0
  • chris01  · 技术社区  · 5 年前

    我有一个表选项卡,每行有一个时间戳(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.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 回复  |  直到 5 年前
        1
  •  2
  •   Andreas LppEdd    5 年前

    时区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 在1970年01月01日,所以没有夏令时。

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

    转换 java.sql.Timestamp时间戳 java.time.Instant 将调整为夏令时,所以混乱的时代。

    请注意 带本地时区的时间戳 不存储时区,它只是告诉客户机API代表您转换到会话时区。数据库中的实际值取决于数据库时区,而不是客户端时区或会话时区。

        2
  •  1
  •   Muhammed Imran Hussain    5 年前

    https://docs.oracle.com/javase/8/docs/api/java/sql/Timestamp.html Timestamp,java.util.Date周围的一个薄包装,它允许JDBCAPI将其标识为一个SQL时间戳值。它增加了保持SQL TIMESTAMP分数秒值的能力,允许将分数秒的指定精确到纳秒。时间戳还提供格式化和解析操作,以支持时间戳值的JDBC转义语法。

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