原文地址html
當JVM時區和數據庫時區不一致的時候,會發生什麼?這個問題也許你歷來沒有注意過,可是當把Java程序容器化的時候,問題就浮現出來了,由於目前幾乎全部的Docker Image的時區都是UTC。本文探究了Oracle及其JDBC驅動對於時區的處理方式,並嘗試給出最佳實踐。java
DATE
和TIMESTAMP
類型不支持時區轉換。若是應用和Oracle的時區不一致,那麼應該使用TIMESTAMP WITH LOCAL TIME ZONE
。git
TIMESTAMP WITH TIME ZONE
。格式化日期時間字符串函數TO_CHAR
:github
TIMESTAMP WITH TIME ZONE
來講,使用TO_CHAR
時要注意讓它輸出時區信息(TZH:TZM TZR TZD
),不然結果會是截斷的。TIMESTAMP WITH LOCAL TIME ZONE
來講,使用TO_CHAR
返回的結果會轉換時區。當前日期時間的函數:sql
SYSDATE
和SYSTIMESTAMP
,這個返回的是數據庫所在操做系統的時間。CURRENT_TIMESTAMP
,它返回的是TIMESTAMP WITH TIME ZONE
,可以用來安全的比較時間。Oracle Datetime Datatypes有這麼幾種:docker
YYYY-MM-DD HH24:MI:SS
。DATE
多存了fractional seconds(FF
)。TIMESTAMP
多了時區偏移量(好比+08:00,TZH:TZM
)or 時區區域名稱(好比Asia/Shanghai,TZR
)和夏令時標記(TZD
)。TIMESTAMP
相似,不過存儲的數據會標準化爲數據庫的時區,用戶獲取它的時候會轉換成用戶時區(對於JDBC來講,就是JVM時區)。docker run --name oracle-xe-timezone-test \ -e ORACLE_ALLOW_REMOTE=true \ -p 1521:1521 \ -d wnameless/oracle-xe-11g:16.04
而後用system/oracle用戶登陸到oracle,執行下列sql建表:數據庫
create table test ( date_field date, ts_field timestamp, ts_tz_field timestamp with time zone, ts_ltz_field timestamp with local time zone );
爲了驗證這個結論,我寫了一段程序來實驗,這個程序作了三件事情:安全
運行程序得到如下結果:bash
JVM Time Zone : 中國標準時間 Retrieve java.util.Date from DATE column : 2018-09-14 10:00:00.0 Retrieve java.util.Date from TIMESTAMP column : 2018-09-14 10:00:00.0 Retrieve java.util.Date from TIMESTAMP WITH TIME ZONE column : 2018-09-14 10:00:00.0 Retrieve java.util.Date from TIMESTAMP WITH LOCAL TIME ZONE column : 2018-09-14 10:00:00.0 Retrieve formatted string from DATE column : 2018-09-14 10:00:00 Retrieve formatted string from TIMESTAMP column : 2018-09-14 10:00:00 Retrieve formatted string from TIMESTAMP WITH TIME ZONE column : 2018-09-14 10:00:00 +08:00 ASIA/SHANGHAI CST Retrieve formatted string from TIMESTAMP WITH LOCAL TIME ZONE column : 2018-09-14 10:00:00 -------------------- JVM Time Zone : 中歐時間 Retrieve java.util.Date from DATE column : 2018-09-14 10:00:00.0 Retrieve java.util.Date from TIMESTAMP column : 2018-09-14 10:00:00.0 Retrieve java.util.Date from TIMESTAMP WITH TIME ZONE column : 2018-09-14 04:00:00.0 Retrieve java.util.Date from TIMESTAMP WITH LOCAL TIME ZONE column : 2018-09-14 04:00:00.0 Retrieve formatted string from DATE column : 2018-09-14 10:00:00 Retrieve formatted string from TIMESTAMP column : 2018-09-14 10:00:00 Retrieve formatted string from TIMESTAMP WITH TIME ZONE column : 2018-09-14 10:00:00 +08:00 ASIA/SHANGHAI CST Retrieve formatted string from TIMESTAMP WITH LOCAL TIME ZONE column : 2018-09-14 04:00:00
能夠看到,DATE
和TIMESTAMP
是不支持時區轉換的,實際上DATE
和TIMESTAMP
會丟棄掉時區信息。session
對於TIMESTAMP WITH TIME ZONE
來講,使用TO_CHAR
時要注意讓它輸出時區信息(TZH:TZM TZR TZD
),不然結果會是截斷的。
對於TIMESTAMP WITH LOCAL TIME ZONE
來講,使用TO_CHAR
返回的結果會轉換時區。
Oracle和當前時間有關的函數有這麼幾個:
CURRENT_DATE
,返回的是DATE
類型CURRENT_TIMESTAMP
,返回的是TIMESTAMP WITH TIME ZONE
類型LOCALTIMESTAMP
,返回的是TIMESTAMP
類型SYSDATE
,返回的是DATE
類型SYSTIMESTAMP
,返回的是TIMESTAMP
類型寫了一段程序,輸出結果是這樣的:
=========TEST CURRENT DATE/TIME FUNCTIONS=========== JVM Time Zone : 中國標準時間 Test CURRENT_DATE : 2018-09-18 10:27:23.0 Test CURRENT_TIMESTAMP : 2018-09-18 10:27:23.880378 Asia/Shanghai Test LOCALTIMESTAMP : 2018-09-18 10:27:23.926375 Test SYSDATE : 2018-09-18 02:27:23.0 Test SYSTIMESTAMP : 2018-09-18 02:27:23.929605 +0:00 -------------------- JVM Time Zone : 中歐時間 Test CURRENT_DATE : 2018-09-18 04:27:45.0 Test CURRENT_TIMESTAMP : 2018-09-18 04:27:45.429024 Europe/Paris Test LOCALTIMESTAMP : 2018-09-18 04:27:45.482485 Test SYSDATE : 2018-09-18 02:27:45.0 Test SYSTIMESTAMP : 2018-09-18 02:27:45.48582 +0:00
能夠發現,CURRENT_DATE
、CURRENT_TIMESTAMP
、LOCALTIMESTAMP
的結果都根據客戶端時區作了轉換。而SYSDATE
和SYSTIMESTAMP
返回的則是數據庫所在操做系統所在時區的時間。
-- 查詢系統時區和session時區 SELECT DBTIMEZONE, SESSIONTIMEZONE FROM DUAL; -- 設置session時區 ALTER SESSION SET TIME_ZONE='Asia/Shanghai';
參見Setting the Database Time Zone 和 Setting the Session Time Zone