最近遇到一个表中数据时区不对的问题,排查思路一般是看表字段类型、看时区以及看插入时间的方法。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
--oracle 修改默认日期格式
alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
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
Function | Description |
ADD_MONTHS | Returns the date
|
LAST_DAY | Returns the last day of the month that contains
|
MONTHS_BETWEEN | Returnsthe number of months between 返回date2与date1间相隔的月份数
|
NEW_TIME | Returns the date and time in 查询指定时区的日期时间在另一指定时区对应的日期时间(各时区时间转换查询)
|
NEXT_DAY | Returns the date of the first weekday named by 返回自输入日期(参数1)开始,参数2的指定星期几对应是几号。 参数2可以用全称如'monday'、可以用缩写如'wed',也可以用数字(星期日 = 1 星期一 = 2 星期二 = 3 星期三 = 4 星期四 = 5 星期五 = 6 星期六 = 7 )
|
ROUND(date) | Returns |
TRUNC(date) | Returns 为指定日期按指定格式而截去后的日期值,语法格式为TRUNC(date[,fmt])
|
2. Additional Datetime Functions
Datetime Function | Description |
CURRENT_DATE | Returns the current date in the session time zone in a value in the Gregorian calendar, of the 以date类型返回当前会话时区中的当前日期
|
CURRENT_TIMESTAMP | Returns the current date and time in the session time zone as a 以timestamp with time zone类型返回当前会话时区中的当前时间
|
DBTIMEZONE | Returns the value. The value is a time zone offset or a time zone region name 返回数据库时区
|
EXTRACT | Extracts and returns the value of a specified datetime field from a datetime or interval value expression 提取指定日期时间的指定部分,比如年、月、日、小时、分钟等
|
FROM_TZ | Converts a |
LOCALTIMESTAMP | Returns the current date and time in the session time zone in a value of the 以timestamp类型返回当前会话中的日期和时间
|
NUMTODSINTERVAL | Converts number |
NUMTOYMINTERVAL | Converts number |
SESSIONTIMEZONE | Returns the value of the current session's time zone 返回当前会话时区,针对当前会话,可以在会话级改变
|
SYS_EXTRACT_UTC | Extracts the UTC from a datetime with time zone offset |
SYSDATE | Returns the date and time of the operating system on which the database resides, taking into account the time zone of the database |
SYSTIMESTAMP | Returns the system date, including fractional seconds and time zone of the system on which the database resides |
TO_CHAR | Converts a datetime or interval value of |
TO_DSINTERVAL | Converts a character string of |
TO_NCHAR | Converts a datetime or interval value of |
TO_TIMESTAMP | Converts a character string of |
TO_TIMESTAMP_TZ | Converts a character string of |
TO_YMINTERVAL | Converts a character string of |
TZ_OFFSET | Returns the time zone offset that corresponds to the entered value, based on the date that the statement is executed |
3. Time Zone Conversion Functions
Time Zone Function | Description |
ORA_DST_AFFECTED | Enables you to verify whether the data in a column is affected by upgrading the DST rules from one version to another version |
ORA_DST_CONVERT | Enables you to upgrade your TSTZ column data from one version to |
ORA_DST_ERROR | Enables you to |
参考
https://docs.oracle.com/en/database/oracle/oracle-database/19/nlspg/datetime-data-types-and-time-zone-support.html#GUID-7A1BA319-767A-43CC-A579-4DAC7063B243
http://blog.itpub.net/29457434/viewspace-1080444/