zoukankan      html  css  js  c++  java
  • 转:Oracle Dates and Timestamps收藏

    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 来操作。

    魔兽就是毒瘤,大家千万不要玩。
  • 相关阅读:
    jquery json 格式教程
    不修改代码就能优化ASP.NET网站性能的一些方法
    C#操作sqlite数据库使用SQLiteParameter传递参数
    60个开发者不容错过的免费资源库
    Java 与 .NET 的平台发展之争
    “一次编写,随处运行” Intel HTML5技术研讨会
    Struts2 高危漏洞修复方案 (S2-016/S2-017)
    AspNetPager 控件使用
    jQueryUI常用功能实战
    验证码生成类
  • 原文地址:https://www.cnblogs.com/tracy/p/1712660.html
Copyright © 2011-2022 走看看