1)DATE
7字节的定宽日期/时间数据类型。
总包含7个属性,包括:世纪、世纪中哪一年、月份、月中的哪一天、小时、分钟和秒。
2)TIMESTAMP [(fractional_seconds_precision)]
7字节或11字节的定宽日期/时间数据类型。
与DATE相比,还可以包含0~9小数位秒(fractional second)。默认6个小数位。
3)TIMESTAMP[(fractional_seconds)] WITH TIME ZONE
13字节的定宽TIMESTAMP,还提供了时区(TIME ZONE)支持。数据中会随TIMESTAMP存储有关时区的额外信息。
4)TIMESTAMP WITH LOCAL TIME ZONE
与TIMESTAMP类似,7字节或11字节的定宽日期/时间数据类型。
存入数据库时,把数据中的日期/时间部分转换为按照数据库时区表示的日期/时间。
获取数据时,把数据中的日期/时间部分转换为按照会话的时区表示的日期/时间。
5)INTERVAL[(year_precision)] YEAR TO MONTH
5字节的定宽数据类型,用于存储一个时间段,这个类型将时段存储为年数和月数。
year_precision可以为0~9个数字,默认值2。
可以在日期运算中使用这种时间间隔使一个DATE 或TIMESTAMP类型增加或减少一段时间。
6)INTERVAL DAY[(day_precision)] TO SECOND [(fractional_seconds)]
11字节的定宽数据类型,用于存储一个时段,这个类型将时段存储为天/小时/分钟/秒数。
day_precision可以为0~9个数字,默认值2。
fractional_seconds可以为0~9个数字,默认值6。
将一个表示DATE、TIMESTAMP 或INTERVAL的串发送到数据库时就应该指定格式。不要依赖于默认格式。
利用TRUNC函数截断DATE
tony@ORA11GR2> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'; Session altered. tony@ORA11GR2> create table t (x date, y varchar2(32)); Table created. tony@ORA11GR2> insert into t values(sysdate, sysdate); 1 row created. tony@ORA11GR2> select trunc(x, 'yyyy'), y from t; TRUNC(X,'YYYY') Y ------------------- -------------------------------- 2011-01-01 00:00:00 2011-10-27 17:13:23 tony@ORA11GR2> select trunc(x, 'mm'), y from t; TRUNC(X,'MM') Y ------------------- -------------------------------- 2011-10-01 00:00:00 2011-10-27 17:13:23 tony@ORA11GR2> select trunc(x, 'mi'), y from t; TRUNC(X,'MI') Y ------------------- -------------------------------- 2011-10-27 17:13:00 2011-10-27 17:13:23 --本季度第一天tony@ORA11GR2> select trunc(x, 'Q'), y from t; TRUNC(X,'Q') Y ------------------- ---------------------------- 2011-10-01 00:00:00 2011-10-27 17:13:23
*trunc(date, fmt),如果省略fmt参数,默认为'dd'.
向DATE加减时间
----------------------------------------------------------------------- 时间单位 操 作 ----------------------------------------------------------------------- n秒 DATE+n/24/60/60 或者 DATE+NUMTODSINTERVAL(n,'second') n分钟 DATE+n/24/60 或者 DATE+NUMTODSINTERVAL(n,'minute') n小时 DATE+n/24 或者 DATE+NUMTODSINTERVAL(n,'hour') n天 DATE+n n周 DATE+n*7 n月 ADD_MONTHS(DATE,n) 或者 DATE+NUMTOYMINTERVAL(n,'month') n年 ADD_MONTHS(DATE,n*12) 或者 DATE+NUMTOYMINTERVAL(n,'year') 下周某一天 next_day(sysdate, '星期一') 或者next_day(sysdate, 2) 本月最后一天 last_day(sysdate) -----------------------------------------------------------------------
注意:
1)next_day和nls_date_languate相关,如果是英文,需要指定为monday,tuesday...
也可以用数字,更通用。--星期日 = 1 星期一 = 2 ... 星期五 = 6 星期六 = 7
2)一般应该使用ADD_MONTHS来处理年和月,而不要使用NUMTOYMINTERVAL,因为这2个函数处理月末日期的行为不一样。
tony@ORA11GR2> select dt, add_months(dt, 1) 2 from (select to_date('2000-02-29', 'yyyy-mm-dd') dt from dual); DT ADD_MONTHS(DT,1) ------------------- ------------------- 2000-02-29 00:00:00 2000-03-31 00:00:00 tony@ORA11GR2> select dt, add_months(dt, 1) 2 from (select to_date('2000-03-31', 'yyyy-mm-dd') dt from dual); DT ADD_MONTHS(DT,1) ------------------- ------------------- 2000-03-31 00:00:00 2000-04-30 00:00:00 tony@ORA11GR2> select dt, dt+numtoyminterval(1,'month') 2 from (select to_date('2000-02-29', 'yyyy-mm-dd') dt from dual); DT DT+NUMTOYMINTERVAL( ------------------- ------------------- 2000-02-29 00:00:00 2000-03-29 00:00:00 tony@ORA11GR2> select dt, dt+numtoyminterval(1,'month') 2 from (select to_date('2000-03-31', 'yyyy-mm-dd') dt from dual); select dt, dt+numtoyminterval(1,'month') * ERROR at line 1: ORA-01839: date not valid for month specified tony@ORA11GR2> select dt, dt+numtoyminterval(1,'year') 2 from (select to_date('2000-02-29', 'yyyy-mm-dd') dt from dual); select dt, dt+numtoyminterval(1,'year') * ERROR at line 1: ORA-01839: date not valid for month specified
计算两个DATE之差
1)两个DATE相减得到相隔天数(包括小数)
2)使用MONTHS_BETWEEN函数得到相隔月数(包括小数)
3)使用INTERVAL类型可以查看2个日期之间的时间
例如,计算2个日期之间相隔的年数、月数、天数、小时数、分钟数和秒数。
tony@ORA11GR2> column years_months format a32; tony@ORA11GR2> column days_hours format a32; tony@ORA11GR2> select numtoyminterval(trunc(months_between(dt2,dt1)), 'month') 2 years_months, 3 numtodsinterval(dt2-add_months(dt1, trunc(months_between(dt2,dt1))), 'day') 4 days_hours 5 from (select to_date('2000-02-29 00:01:02', 'yyyy-mm-dd hh24:mi:ss') dt1, 6 to_date('2002-02-01 01:02:01', 'yyyy-mm-dd hh24:mi:ss') dt2 7 from dual) 8 / YEARS_MONTHS DAYS_HOURS -------------------------------- -------------------------------- +000000001-11 +000000001 01:00:59.000000000
向TIMESTAMP 加减时间
DATE执行日期算术运算所用的方法适用于TIMESTAMP,但是直接加减数字和add_months会导致TIMESTAMP到DATE的隐式转换。
所以一般使用INTERVAL来进行时间加减,但是需要注意月末日期可能导致的问题。
*可以用cast来进行两者之间的显式转换。
tony@MYTEST2> select systimestamp, systimestamp+1/24 from dual; SYSTIMESTAMP SYSTIMESTAMP+1/24 ---------------------------------------- ------------------- 28-OCT-11 01.33.34.090000 PM +08:00 2011-10-28 14:33:34 tony@MYTEST2> select systimestamp, add_months(systimestamp,1) from dual; SYSTIMESTAMP ADD_MONTHS(SYSTIMES ---------------------------------------- ------------------- 28-OCT-11 01.33.36.606000 PM +08:00 2011-11-28 13:33:36 tony@MYTEST2> select systimestamp, systimestamp+numtoyminterval(1,'month') from dual; SYSTIMESTAMP SYSTIMESTAMP+NUMTOYMINTERVAL(1,'MONTH') ------------------------------------- ------------------------------------- 28-OCT-11 01.37.21.920000 PM +08:00 28-NOV-11 01.37.21.920000000 PM +08:00 select cast(systimestamp as date) from dual; select cast(sysdate as timestamp) from dual;
计算两个TIMESTAMP之差
这一点上,DATE和TIMESTAMP类型存在显著差异。
将DATE相减的结果是一个NUMBER(间隔天数),但TIMESTAMP相减的结果却是一个INTERVAL。
tony@MYTEST2> declare 2 l_past_time timestamp; 3 l_current_time timestamp; 4 begin 5 select systimestamp into l_past_time from dual; 6 dbms_lock.sleep(1); 7 select systimestamp into l_current_time from dual; 8 dbms_output.put_line(l_current_time - l_past_time); 9 end; 10 / +000000000 00:00:01.000000000 PL/SQL procedure successfully completed.
支持时区(TIME ZONE)的TIMESTAMP类型
有两种支持时区的TIMESTAMP类型:TIMESTAMP WITH TIME ZONE和TIMESTAMP WITH LOCAL TIME ZONE。
TIMESTAMP WITH TIME ZONE类型存储的时候保留了时区信息,TIMESTAMP WITH LOCAL TIME ZONE则根据会话时区和数据库时区,将时间转换为数据库时区后进行存储,读取时则转换为相应的会话时区表示的时间。
如果不需要记住源时区,只需要这样一种数据类型,要求能对日期/时间类型提供一致的全球性处理,那么TIMESTAMP WITH LOCAL TIME ZONE 对大多数应用来说已经能提供足够的支持。
可以用TZ_OFFSET 查询某时区和UTC 之间的差值。
TZ_OFFSET ( { 'time_zone_name' | '{ + | - } hh : mi' | SESSIONTIMEZONE | DBTIMEZONE } )
合法的'time_zone_name'可以通过查询V$TIMEZONE_NAMES表得到。
数据库时区可以在创建数据库时用set time_zone=...子句来指定。默认是+00:00。
可以用alter session/database set time_zone=...语句来更改时区。
可以用以下语句查询数据库时区和会话时区。
tony@MYTEST2> select dbtimezone, sessiontimezone from dual; DBTIME SESSIONTIMEZONE ------ -------------------- +00:00 +08:00
可以用at time zone'语句转换为指定时区的时间。例如select systimestamp at time zone 'GMT' from dual;
下面的例子可以看出来time zone和local time zone这两种类型的区别。
tony@MYTEST2> create table t( 2 ts timestamp, 3 ts_timezone timestamp with time zone, 4 ts_local timestamp with local time zone); Table created. tony@MYTEST2> insert into t values( 2 to_timestamp('2000-02-29 00:00:00.000', 'yyyy-mm-dd hh24:mi:ss.ff'), 3 to_timestamp_tz('2000-02-29 00:00:00.000 +08:00', 'yyyy-mm-dd hh24:mi:ss.ff tzh:tzm'), 4 to_timestamp('2000-02-29 00:00:00.000', 'yyyy-mm-dd hh24:mi:ss.ff')); 1 row created. tony@MYTEST2> alter session set nls_timestamp_format='yyyy-mm-dd hh24:mi:ss.ff'; Session altered. tony@MYTEST2> alter session set nls_timestamp_tz_format='yyyy-mm-dd hh24:mi:ss.ff tzh:tzm'; Session altered. tony@MYTEST2> select * from t; TS TS_TIMEZONE ------------------------------ ---------------------------------------- TS_LOCAL ------------------------------ 2000-02-29 00:00:00.000000 2000-02-29 00:00:00.000000 +08:00 2000-02-29 00:00:00.000000 tony@MYTEST2> alter session set time_zone='+7:00'; Session altered. tony@MYTEST2> select * from t; TS TS_TIMEZONE ------------------------------ ---------------------------------------- TS_LOCAL ------------------------------ 2000-02-29 00:00:00.000000 2000-02-29 00:00:00.000000 +08:00 2000-02-28 23:00:00.000000
此外,一旦在数据库中创建了包含TIMESTAMP WITH LOCAL TIME ZONE类型数据的表格,并插入了数据,就不能更改数据库时区了。
因为如果可以修改,在新时区下,无法得到正确时间。
tony@MYTEST2> conn / as sysdba Connected. sys@MYTEST2> alter database set time_zone='+08:00'; alter database set time_zone='+08:00' * ERROR at line 1: ORA-30079: cannot alter database timezone when database has TIMESTAMP WITH LOCAL TIME ZONE columns
关于DST(daylight saving time)
DST就是我们说的夏时制。比如美国的夏时制
http://www.timetemperature.com/tzus/daylight_saving_time.shtml
2014年3月9号早上2点开始时钟拨快1小时。
指定一个时区的时候,可以使用地区名称(US/Eastern),缩写(EST)或者数字(-05:00)。(可以通过V$TIMEZONE_NAMES表查看名称和缩写)。
在多数情况下,它们的效果是一样的。但是在DST的处理过程中,如果使用地区名称来指定时区,ORACLE会自动进行DST的转换。
这个时候,2014年3月9号早上2点就是一个不存在的时间,如果指定这样一个时间转换为timestamp,就会报错。
scott@ORA11GR2> select to_timestamp_tz('2014-03-09 01:00:00 US/Eastern', 2 'yyyy-mm-dd hh24:mi:ss tzr') + 3 to_dsinterval('0 01:00:00') 4 from dual; TO_TIMESTAMP_TZ('2014-03-0901:00:00US/EASTERN','YYYY-MM-DDHH24:MI:SSTZR')+T --------------------------------------------------------------------------- 09-MAR-14 03.00.00.000000000 AM US/EASTERN scott@ORA11GR2> scott@ORA11GR2> select to_timestamp_tz('2014-03-09 01:00:00 EST', 2 'yyyy-mm-dd hh24:mi:ss tzr') + 3 to_dsinterval('0 01:00:00') 4 from dual; TO_TIMESTAMP_TZ('2014-03-0901:00:00EST','YYYY-MM-DDHH24:MI:SSTZR')+TO_DSINT --------------------------------------------------------------------------- 09-MAR-14 02.00.00.000000000 AM EST scott@ORA11GR2> scott@ORA11GR2> select to_timestamp_tz('2014-03-09 01:00:00 -5:00', 2 'yyyy-mm-dd hh24:mi:ss tzh:tzm') + 3 to_dsinterval('0 01:00:00') 4 from dual; TO_TIMESTAMP_TZ('2014-03-0901:00:00-5:00','YYYY-MM-DDHH24:MI:SSTZH:TZM')+TO --------------------------------------------------------------------------- 09-MAR-14 02.00.00.000000000 AM -05:00 scott@ORA11GR2> scott@ORA11GR2> select to_timestamp_tz('2014-03-09 02:00:00 US/Eastern', 2 'yyyy-mm-dd hh24:mi:ss tzr') + 3 to_dsinterval('0 01:00:00') 4 from dual; select to_timestamp_tz('2014-03-09 02:00:00 US/Eastern', * ERROR at line 1: ORA-01878: specified field not found in datetime or interval
INTERVAL YEAR TO MONTH类型
可以使用以下办法创建INTERVAL YEAR TO MONTH类型的实例
1)NUMTOYMINTERVAL 函数
2) TO_YMINTERVAL函数
3) 直接在SQL中使用INTERVAL类型
例如,
tony@MYTEST2> select numtoyminterval(1,'year') 2 + numtoyminterval(2,'month') from dual; NUMTOYMINTERVAL(1,'YEAR')+NUMTOYMINTERVAL(2,'MONTH') ---------------------------------------------------- +000000001-02 tony@MYTEST2> select to_yminterval('1-2') from dual; TO_YMINTERVAL('1-2') ---------------------------------------------------- +000000001-02 tony@MYTEST2> select interval '5-2' year to month from dual INTERVAL'5-2'YEARTOMONTH ----------------------------------------------------------- +05-02
INTERVAL DAY TO SECOND类型
可以使用以下办法创建INTERVAL DAY TO SECOND类型的实例
1)NUMTODSINTERVAL 函数
2) TO_DSINTERVAL函数
3) 直接在SQL中使用INTERVAL类型
tony@MYTEST2> select numtodsinterval(1,'day') 2 + numtodsinterval(12,'hour') from dual; NUMTODSINTERVAL(1,'DAY')+NUMTODSINTERVAL(12,'HOUR') ----------------------------------------------------------------------- +000000001 12:00:00.000000000 tony@MYTEST2> select to_dsinterval('1 12:10:10.123') from dual; TO_DSINTERVAL('112:10:10.123') ----------------------------------------------------------------------- +000000001 12:10:10.123000000 tony@MYTEST2> select interval '1 12:10:10.123' day to second from dual; INTERVAL'112:10:10.123'DAYTOSECOND ----------------------------------------------------------------------- +01 12:10:10.123000
EXTRACT 函数
可以使用EXTRACT函数从DATE, TIMESTAMP 和INTERVAL中返回各部分信息。
EXTRACT(
{ YEAR | MONTH | DAY |HOUR | MINUTE | SECOND } |
{ TIMEZONE_HOUR |TIMEZONE_MINUTE } |
{ TIMEZONE_REGION }|
TIMEZONE_ABBR }
FROM x)
例如:
tony@MYTEST2> select extract(year from sysdate) from dual; EXTRACT(YEARFROMSYSDATE) ------------------------ 2011 tony@MYTEST2> select extract(timezone_hour from systimestamp) from dual; EXTRACT(TIMEZONE_HOURFROMSYSTIMESTAMP) -------------------------------------- 8