代码改变世界

转:Oracle Dates and Timestamps收藏

2010-04-15 14:46  Tracy.  阅读(1295)  评论(0编辑  收藏  举报

Dates and Timestamps

转自:http://www.heysky.net/cgi-bin/mt/mt-tb.cgi/408


1.Datetime Datatypes

    DATE

      存储日期、时间,没有时区,精确到秒,是 9i 之前唯一的 datetime datatype

    TIMESTAMP [(precision)]

      除了最多精确到 billionth of a second,其他和 DATE 一样。

    TIMESTAMP [(precision)] WITH TIME ZONE

      TIMESTAMP 的基础上,保存时区信息。在内部存储时,Oracle 将时间转换为 UTC 格式进行保存,比如:2002-02-06 20:00:00:00.00 -5:00

    TIMESTAMP [(precision)] WITH LOCAL TIME ZONE

      不保存时区信息,但会将时间转换为数据库的时区(如果是保存到数据库表的列中)或者 session 的时区(如果保存为 PL/SQL variables)。数据在不同时区间传输时,会进行转换,但不保存时区信息。

    其中 precision 表示秒的小数部分保留的位数,范围为:0~9

   

    如何选择 Datetime Datatype

      1.如果你要精确到秒的小数位,那么使用 TIMESTAMP

      2.如果要保留 datetime 值的时区,那么使用 TIMESTAMP WITH TIME ZONE

      3.可以用 TIMESTAMP(0) 代替 DATE,但是两者的日期算法是不同的

      4.为了兼容 TIMESTAMP 出现之前的应用程序,那么使用 DATE

      5.PL/SQL 代码中的类型应该和数据库表中的类型相一致,比如将 TIMESTAMP WITH TIME ZONE 的类型存放到 DATE 类型的列中,时区就会丢失

      6.使用 9i 以前的版本,那么只能使用 DATE

      7. ADD_MONTHS 这种传统操作 DATE 类型的函数应用到新的 TIMESTAMP 类型上,会产生很大的不同

     



2.获得现在的时间

  Function            Time zone    Datatype returned

  CURRENT_DATE        Session      DATE

  CURRENT_TIMESTAMP   Session      TIMESTAMP WITH TIME ZONE

  LOCALTIMESTAMP      Session      TIMESTAMP

  SYSDATE             Server       DATE

  SYSTIMESTAMP        Server       TIMESTAMP WITH TIME ZONE



  注:返回的都是数据库服务器端的当时时间,单前三者会转换为 session 的时区,可以用 alter session set time_zone 改变

      9i 之前只有 SYSDATE

 



3.Interval Datatypes

    INTERVAL YEAR TO MONTH

      Allows you to define an interval of time in terms of years and months.

      INTERVAL YEAR [(year_precision)] TO MONTH

        year_precisionyear 的位数,范围:0~4,默认:2

    INTERVAL DAY TO SECOND

      Allows you to define an interval of time in terms of days, hours, minutes, and seconds (including fractional seconds).

      INTERVAL DAY [(day_precision)] TO SECOND [(frac_sec_prec)]

        day_precisionday 的位数,范围:0~9,默认:2

        frac_sec_prec:秒小数部分保留的位数,范围:0~9,默认:6

       

    Monthhourminutesecond 的精度是不用设置的,Oracle 会保证他们的范围分别在 0~110~230~590~59



4.Datetime Conversions

    Date 范围:

      4712-01-01 B.C. —— 9999-12-31 A.D.

     

    From Strings to Datetimes

      隐式:

        根据 NLS_DATE_FORMAT 的格式写 String 的值,Oracle 会隐式转换,如果和 NLS_DATE_FORMAT 不匹配,不能转换。

      显式(使用内建函数):

        TO_DATE( string[, format_mask[, nls_language]])

        TO_DATE( number[, format_mask[, nls_language]])

          用数字表示 Julian date 转换为 Date 类型,此时 format_mask = 'J'number 表示从 January 1, 4712 B.C. 开始的天数,由于 Oracle 中最大日期是 December 31, 9999 A.D.,所以 number 的范围为:1 ~ 5373484

        TO_TIMESTAMP( string[, format_mask[, nls_language]])

        TO_TIMESTAMP_TZ( string[, format_mask[, nls_language]])

          此函数用于将 string 转换为 TIMESTAMP WITH TIME ZONETIMESTAMP WITH LOCAL TIME ZONE

        format_mask

          默认为 NLS_DATE_FORMATNLS_TIMESTAMP_FORMATNLS_TIMESTAMP_TZ_FORMAT(分别对应 TO_DATETO_TIMESTAMPTO_TIMESTAMP_TZ

          对于 TIMESTAMP 类型,秒的小数部分可以用 '.FF' 或者 'XFF' 表示,比如:'mm/dd/yyyy hh:mi:ss.ff AM TZD' or 'mm/dd/yyyy hh:mi:ssxff AM TZD',其中 'X' NLS_NUMERIC_CHARACTERS 的第一个字符决定。

        nls_language

          Optionally specifies the language to be used to interpret the names and abbreviations of both months and days in the string.

        几个限制:

          1.传给 TO_DATE 中的 string 长度不能超过 220 个字符。

          2.format mask Julian date element (J) the day of year element (DDD) 不能同时出现。

          3.format mask date/time 的某一个部分不能重复出现,比如:'YYYY-YYY-DD-MM'

          4.format mask HH24 不能和 am/pm 同时出现

         

    From Datetimes to Strings

      使用 TO_CHAR,默认格式 'DD-MON-RR'9 位),可以用 NLS_DATE_FORMAT 覆盖。

      对于 TIMESTAMP 类型,秒的小数部分可以用 FF1 ~ FF9 来表示保留几位(自动四舍五入)。

      不能将用于 TIMESTAMP format_mask 用于 DATE 类型,否则会报 ORA-01821,反过来可以。

     

    Working with Time Zones

      明确表示某一个时区,应该联合使用 TZH TZM 或者 TZR TZD

        TZH:与 UTC 之间 HOUR 的偏移

        TZM:与 UTC 之间 MINUTE 的偏移

        TZRThe time zone region

        TZDThe abbreviated time zone name

        注:后两者可以查看 V$TIMEZONE_NAMES 获得

            对于时区,存入的是什么信息,显示的也是什么信息,比如用 TZHTZM 存入和 UTC 之间的偏移,就只能显示类似 +0800 的时区偏移,而无法显示具体哪个 time zone region

       

    二位数年份的处理

      使用 RR/RRRR 时,Oracle 自动根据现在的年份辨别输入的二位数年份:

        1.如果现在是前半世纪(0~49

          如果输入的是前半世纪(0~49),那么返回本世纪的年份

          如果输入的是后半世纪(50~99),那么返回上世纪的年份

        2.如果现在是后半世纪(50~99

          如果输入的是前半世纪(0~49),那么返回下世纪的年份

          如果输入的是后半世纪(50~99),那么返回本世纪的年份

      使用 YY/YYYY 时,不管输入的是什么,都返回本世纪的年份

      注:这种自动转换只适用于 String -> Date 的转换,如果是 Date -> String,那就按照 Date 存储的值来转换,此时再用 RR/RRRR 已经没有意义了,因为 Oracle 内部存储的年份是四位数的。

         



5.Date and Timestamp Literals

    这是 9i 之后 引入的 ISO SQL standard 格式,格式是固定的,不能更改,也不受环境变量影响,因此可以作为常量来使用

      DATE 'YYYY-MM-DD'

      TIMESTAMP 'YYYY-MM-DD HH:MI:SS[.FFFFFFFFF] [{+|-}HH:MI]' HH 必须是 24 小时制的,FFFFFFFFF 可选 1~9 位,也可以没有,时区也可以使用 time zone regionEST),但这只是 Oracle 提供的格式,不是 ANSI/ISO standards

    例子:

      DATE '2002-02-19'

      TIMESTAMP '2002-02-19 14:00:00.000000000 -5:00';

     

     

6.Interval Conversions

    Numbers to Intervals

      NUMTOYMINTERVAL ( n , 'char_expr' )

      NUMTODSINTERVAL ( n , 'char_expr' )

      char_expr

      Name       Description

      YEAR       Some number of years, ranging from 1 through 999,999,999

      MONTH      Some number of months, ranging from 0 through 11

      DAY        Some number of days, ranging from 0 to 999,999,999

      HOUR       Some number of hours, ranging from 0 through 23

      MINUTE     Some number of minutes, ranging from 0 through 59

      SECOND     Some number of seconds, ranging from 0 through 59.999999999

      以上不区分大小写

     

    Strings to Intervals

      TO_YMINTERVAL('Y-M')

      TO_DSINTERVAL('D HH:MI:SS')

      格式是固定的,不能缺少任何一个部分

 

 

7.Interval Literals

    语法: INTERVAL 'character_representation' start_element TO end_element

           character_representation 不需要指明所有 datetime 的元素,但必须指明从 start_element end_element 的所有连续元素,如果只有 start_element 只需要一个元素就行了,但 start_element end_element 不能跨越 month day

           注:由于 bug 的问题,在 9i Releases 1 and 2 10g Release 1 中在 pl/sql 中指明部分元素会出错,比如 INTERVAL '1:02' HOUR TO MINUTE,但在 SQL 中不会。

           Oracle 自动会将 high-end value 规格化,比如:INTERVAL '72:15' HOUR TO MINUTE 会规格化为 +03 00:15:00.000000,但 INTERVAL '72:75' HOUR TO MINUTE 会报错,只有 high-end value(这里的 HOUR)会自动转换

          

          

8.CAST and EXTRACT

    两者都是 standard SQL functions

   

    CASTOracle8 开始)

      语法:

        CASTvar as type

        注:在 SQL 中,type 可以指定长度,比如:varchar2(40),但在 pl/sql 中不能指定长度

      可以在 stringdatetimeDATETIMESTAMP)之间互相转换

      由于无法指定 format,所以取决于 NLS_DATE_FORMATNLS_TIMESTAMP_FORMATNLS_TIMESTAMP_TZ_FORMAT 环境变量


    EXTRACTOracle9i 开始)

      语法:

        EXTRACT (component_name, FROM {datetime | interval})

        component_name(不区分大小写):

        Component name     Return datatype

        YEAR               NUMBER

        MONTH              NUMBER

        DAY                NUMBER

        HOUR               NUMBER

        MINUTE             NUMBER

        SECOND             NUMBER

        TIMEZONE_HOUR      NUMBER

        TIMEZONE_MINUTE    NUMBER

        TIMEZONE_REGION    VARCHAR2

        TIMEZONE_ABBR      VARCHAR2

       

      由于用 to_char 无法格式化 interval 的显示格式(格式是固定的),所以唯一的方法就是使用 EXTRACT 来格式化

 



9.Datetime Arithmetic

    Adding and Subtracting Intervals to/from Datetimes

      1. Intervals +- 操作。在和 INTERVAL DAY TO SECOND 进行运算时,不用考虑任何问题,但和 INTERVAL YEAR TO MONTH 做运算时,由于它是直接 +- 原始 datetimes 的年和月,所以可能够产生 ORA-01839 错误,应该有相关的 exception 处理语句。

      2.直接和 number +- 操作。number 表示天数,可以用分数小数表示:

        Value    Expression   Represents

        1/24     1/24         One hour

        1/1440   1/24/60      One minute

        1/86400  1/24/60/60   One second

        注:不建议进行约分等操作,这样使得程序一目了然。

      3.使用 ADD_MONTHS 函数。

          1.如果输入的日期是输入月份中的最后一天,那么结果也是结果月份的最后一天

            比如:

              add_months(Date '2006-02-28',1)  --> 2006-03-31

          2.如果结果月份的最后一天小于输入月份的日子,那么结果日期是结果月份的最后一天

            比如:

              add_months(Date '2006-03-30',-1)  --> 2006-02-28

         

          可以构建一个自定义函数,解决 1 的问题,让他不返回月份的最后一天

            FUNCTION my_add_months (

               date_in IN DATE, months_shift IN NUMBER)

            RETURN DATE IS

               date_out DATE;

               day_in NUMBER;

               day_out NUMBER;

            BEGIN

               date_out := ADD_MONTHS(date_in, months_shift);

               day_in := TO_NUMBER(TO_CHAR(date_in,'DD'));

               day_out := TO_NUMBER(TO_CHAR(date_out,'DD'));

               IF day_out > day_in

               THEN

                  date_out := date_out - (day_out - day_in);

               END IF;

               RETURN date_out;

            END;

      建议:

        虽然以上三者 DATE TIMESTAMP 都可以操作,但建议 TIMESTAMP 使用 1DATE 可以使用 23

           

    Computing the Interval Between Two Datetimes

      两个 TIMESTAMP 相减返回的永远是 INTERVAL DAY TO SECOND

      两个 DATE 相减返回的是天数,如果有小数部分,意思是 hours, minutes, and seconds 转换为天的结果

      MONTHS_BETWEEN 函数:

        定义:

              FUNCTION MONTHS_BETWEEN (date1 IN DATE, date2 IN DATE)

                RETURN NUMBER

              尝试了一下也可以使用 TIMESTAMP,但还是建议不使用,不知道会出现什么问题。

        1.date1 > date2 返回正数,小于返回负数,相等为0

        2.如果在同一年的同一月中,结果的范围是 > -1 and < 11 -1 都是不会到达的,一月按照 31 天计算,TIME 部分也要计算

        3.如果 date1 date2 分别是相应月份中的第一天或者最后一天,则返回整数,且忽略 TIME 部分的值

        4.如果 date1 date2 在不同的月份中,且至少有一个不是该月的第一天或者最后一天,那么返回小数。小数是基于 31 天为一个月计算的,且要计算 TIME 部分。

       

    Mixing DATEs and TIMESTAMPs

      由前面可知:TIMESTAMPs 相减返回 INTERVAL DAY TO SECONDDATEs 相减返回 numeric value

      那么:

        1.如果 DATEs 相减想返回 INTERVAL DAY TO SECOND 需要用 CAST 显示地将 DATEs 转换为 TIMESTAMPs

            CAST(a AS TIMESTAMP) - CAST(b AS TIMESTAMP)

        2.如果 DATE TIMESTAMP 混合使用,Oracle 隐式地将 DATE 转换为 TIMESTAMP,因此返回的是 INTERVAL DAY TO SECOND

       

    Adding and Subtracting Intervals

      INTERVAL 相加减必须满足类型相同:两个 INTERVAL DAY TO SECOND 相加减,或者两个 INTERVAL YEAR TO MONTH 相加减,不能混合使用。

     

    Multiplying and Dividing Intervals

      DATETIMEs 是不能进行乘除运算的,但是 Intervals 可以。进行乘除运算时,每一个元素都会进行运算,如果超过该元素的范围就往上一级元素进位,如果出现小数就将小数部分转化为下一级元素,秒除外。

   

    Using Unconstrained INTERVAL Types

      由于在函数、过程中,参数不能指定精度,因此当 INTERVAL 传入时如果精度大于默认精度:YEAR(2),DAY(2),SECOND(6),会返回错误 ORA-01873: the leading precision of the interval is too small

      因此,引入两个特殊类型:

        YMINTERVAL_UNCONSTRAINED

          接受任何精度的 INTERVAL YEAR TO MONTH

        DSINTERVAL_UNCONSTRAINED

          接受任何精度的 INTERVAL DAY TO SECOND


 

10.Date/Time Functions

    对于传统的 DATE 函数,比如 ADD_MONTHS 建议不要用于 TIMESTAMPOracle 会将他们隐式地转为 DATE,这样会丢失秒的小数部分,还会将时区修改为 SESSION 的时区。因此,对于 TIMESTAMP 应该使用 INTERVAL 来操作。