原文地址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