最近遇到一个表中数据时区不对的问题,排查思路一般是看表字段类型、看时区以及看插入时间的方法。Oracle官方文档《Database Globalization Support Guide》里有很详细的介绍,归纳学习一下。
一、 时间类型
Oracle里的时间类型分两大类 —— Datetime 和 Interval Data Types,本文主要关注第一类Datetime。
Datetime又可以分为四类,其中与时区有关的是后两类:
- DATE
- TIMESTAMP
- TIMESTAMP WITH TIME ZONE
- TIMESTAMP WITH LOCAL TIME ZONE
1. DATE
存储日期+时间,精确到秒,不存储时区和地区信息。输出格式和语言由 NLS_DATE_FORMAT和NLS_DATE_LANGUAGE 两个初始化参数决定。如果查询时不指定这两个参数也不进行类型转换,会按默认格式输出。
SQL> select sysdate from dual;
SYSDATE
-------------------
2014-02-12 01:12:18
2. TIMESTAMP
DATE类型的扩展,存储日期+时间,可精确到秒后0~9位小数点(默认是6),也不存储时区和地区信息。输出格式和语言由 NLS_TIMESTAMP_FORMAT和NLS_DATE_LANGUAGE 两个初始化参数决定。如果查询时不指定这两个参数也不进行类型转换,会按默认格式输出。
SQL> select localtimestamp from dual;
LOCALTIMESTAMP
---------------------------------------------------------------------------
12-FEB-14 01.14.12.945256 AM
SQL> alter session set nls_timestamp_format='YYYY-MM-DD HH24:MI:SSXFF';
Session altered.
SQL> select localtimestamp from dual;
LOCALTIMESTAMP
---------------------------------------------------------------------------
2014-02-12 01:28:31.652888
3. TIMESTAMP WITH TIME ZONE
TIMESTAMP类型的扩展,存储日期+时间,可精确到秒后0~9位小数点(默认是6),存储时区(或时区和地区)信息。此类型的数据在保存到数据库时带有当前客户端的session timezone,无论在什么时区查看这些数据,数据都不会随时区而变化。
create table t1 (id number,time timestamp with time zone); --创建t1表,其中time 列的数据类型是timestamp with time zone
Table created.
select sessiontimezone from dual; --当前客户端的session timezone 是 -8:00
SESSIONTIMEZONE
---------------------------------------------------------------------------
-08:00
insert into t1 values(1,timestamp '2014-02-12 02:00:00'); --向t1表中插入一条数据
1 row created.
select * from t1; --查看t1表,其中time列带时区显示,并且时区为数据被插入时的session timezone
ID TIME
---------- ---------------------------------------------------------------------------
1 2014-02-12 02:00:00.000000 -08:00
alter session set time_zone='-6:00'; --修改当前客户端的session timezone为 -6:00
Session altered.
select * from t1; --再次查看t1表,其中time列数据无变化
ID TIME
---------- ---------------------------------------------------------------------------
1 2014-02-12 02:00:00.000000 -08:00
4. TIMESTAMP WITH LOCAL TIME ZONE
TIMESTAMP类型的另一种扩展,存储日期+时间,可精确到秒后0~9位小数点(默认是6),不存储时区信息,而是将客户端输入的时间基于database timezone转换后存入数据库(这也就是database tmiezone设置的意义所在,作为TIMESTAMP WITH LOCAL TIME ZONE类型的计算标尺)。当用户查询此类型数据时,Oracle会把数据再转为用户session的时区时间返回给用户。
客户端A时区时间 -> 数据库database tmiezone设置的时区时间 -> 客户端B时区时间
create table t2(id number,time timestamp with local time zone); -- 创建t2表,其中time列为TIMESTAMP WITH LOCAL TIME ZONE
Table created.
insert into t2 values(1,timestamp '2014-02-12 02:10:00 -8:00'); --在t2表插入数据指定时区为-8:00,实际在保存到数据库时转化为基于database timezone的时间保存
1 row created.
select sessiontimezone from dual; --当前客户端的session timezone 为 -6:00
SESSIONTIMEZONE
---------------------------------------------------------------------------
-06:00
select * from t2; --查看时oracle将数据转换成当前客户端session timezone的时间
ID TIME
---------- ---------------------------------------------------------------------------
1 2014-02-12 04:10:00.000000
5. 时间类型的选择
- DATE:需要的时间精度不高,不需要保存时区/地区信息
- TIMESTAMP:需要的时间精度高,不需要保存时区/地区信息
- TIMESTAMP WITH TIME ZONE:需要保存时区/地区信息。比如需要精确记录每一笔交易的时间和地点(时区),看它是在当地几点发生的
- TIMESTAMP WITH LOCAL TIME ZONE:不关心操作发生的地点,只关心操作是在你当前所在时区几点发生的。比如有一部电视剧在日本时间十点开播,但其实我只关心在中国时间几点能一起追直播,对我来说最方便的就是一查数据库直接告诉我它在中国时间九点开播。
二、 时区
其实根据前面一节已经知道了,Oracle时区分两种 —— 数据库时区和会话时区
1. 数据库时区
作为TIMESTAMP WITH LOCAL TIME ZONE类型的计算标尺。
查询方法
SELECT dbtimezone FROM DUAL;
设置方法
- 可以在CREATE DATABASE 时用 SET TIME_ZONE子句指定。
CREATE DATABASE db01
...
SET TIME_ZONE='Europe/London';
-- 或者
CREATE DATABASE db01
...
SET TIME_ZONE='-05:00';
- 也可后期修改(重启DB生效)
ALTER DATABASE SET TIME_ZONE='Europe/London';
--或者
ALTER DATABASE SET TIME_ZONE='-05:00';
2. 会话时区
当前sql会话所在时区,默认是服务器操作系统所在时区。
查询方法
SELECT sessiontimezone FROM DUAL;
设置方法
- 可以设置操作系统的 ORA_SDTZ 环境变量
setenv ORA_SDTZ 'OS_TZ' #默认
setenv ORA_SDTZ 'DB_TZ'
setenv ORA_SDTZ 'Europe/London'
setenv ORA_SDTZ '-05:00'
- 也可以用sql命令设置
ALTER SESSION SET TIME_ZONE=local; -- 相当于os
ALTER SESSION SET TIME_ZONE=dbtimezone;
ALTER SESSION SET TIME_ZONE='Asia/Hong_Kong';
ALTER SESSION SET TIME_ZONE='+10:00';
三、 时间相关函数
Datetime函数可操作 date (DATE), timestamp (TIMESTAMP, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE) 及 interval (INTERVAL DAY TO SECOND, INTERVAL YEAR TO MONTH) 类型的值。
1. Datetime Functions Designed for the DATE Data Type
2. Additional Datetime Functions
3. Time Zone Conversion Functions
参考