Oracle的默认日期格式

25

第一次使用 Oracle SQL(我习惯于使用 MySQL)。 我发现关于默认日期格式有冲突的信息。在多次尝试使用TO_DATE与我的INSERT INTO my_table语句后,我终于发现我正在使用的数据库期望DD-MON-YY(即25-JAN-18)。然而,在stackoverflow和其他页面上,我看到有些人说默认值是YYYYMMDD或DD/MM/YYYY或YYYY-MM-DD。为什么会有这么多不一致的信息呢?


如果您在其他地方找到了冲突的信息,请参考官方Oracle文档 - krokodilko
永远不要使用没有格式掩码的 to_date() 函数 - 或者更好的方法是使用 ANSI SQL 字面量 date '2018-01-25'timestamp '2018-05-04 08:06:00' - user330315
@Pancho:是的,但NLS_DATE_FORMAT取决于运行查询的客户端。即使我们都连接到同一个服务器,当你从你的计算机运行它时可以正常工作的隐式转换,在我从我的计算机运行它时可能无法正常工作。 - user330315
@a_horse_with_no_name - 谢谢 - 抱歉我的问题表述不清。我指的不是客户端运行的即席SQL(我理解这可以随时更改)。我的问题涉及包体内的转换。据我所知,这些转换不受相同的变化影响? - Pancho
我非常确定当您运行一个过程时,客户端的NLS设置会生效。因此,如果我从我的计算机调用该过程,它可能仍然会失败,尽管在您运行它时不会失败。 - user330315
显示剩余4条评论
4个回答

26

DATE没有格式 - 它以7个字节的形式存储,表示年份(2个字节)和月份、日期、小时、分钟和秒(每个1个字节)。

'25-JAN-18'不是日期 - 它是文本字面量。

当你这样做:

INSERT INTO table_name ( date_column ) VALUES ( '25-JAN-18' );

Oracle会尝试帮助并执行从字符串到日期的隐式转换,使用用户会话的NLS_DATE_FORMAT参数作为格式模型。因此,您的语句将被隐式转换为:

INSERT INTO table_name ( date_column ) VALUES (
  TO_DATE(
    '25-JAN-18',
    ( SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT' )
  )
);

任何用户都可以在自己的会话中设置自己的NLS参数(因此您不应该依赖隐式转换,因为每个用户可以为自己的会话设置不同的设置,并且可以在会话过程中更改值)。相反,您应该:

  • Use a Date literal:

    DATE '2018-01-25'
    
  • Use a Timestamp literal

    TIMESTAMP '2018-01-25 01:23:45'
    
  • Use TO_DATE( date_string, format_string [, nls_values] ) and explicitly use a format model:

    TO_DATE( '25-JUN-18', 'DD-MON-RR' )
    

如果您确实想在会话中更改NLS_DATE_FORMAT,则可以使用:

ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

默认的日期格式是什么?
由于DATE类型没有格式,所以这个问题没有意义。如果我们问:
Oracle用于在字符串和日期之间转换时使用的默认NLS_DATE_FORMAT会话参数是什么?
它取决于NLS_TERRITORY会话参数(因此它取决于您在世界的哪个地方)。
SET SERVEROUTPUT ON;

VARIABLE cur REFCURSOR;

DECLARE
  territories SYS.ODCIVARCHAR2LIST;
  formats     SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
BEGIN
  select value
  BULK COLLECT INTO territories
  from v$nls_valid_values
  where parameter = 'TERRITORY'
  order by value;

  formats.EXTEND( territories.COUNT );
  FOR i IN 1 .. territories.COUNT LOOP
    EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TERRITORY='''||territories(i)||'''';

    SELECT value
    INTO   formats(i)
    FROM   NLS_SESSION_PARAMETERS
    WHERE  PARAMETER = 'NLS_DATE_FORMAT';
  END LOOP;

  OPEN :cur FOR
  SELECT CAST( f.format AS VARCHAR2(12) ) AS format,
         LISTAGG( t.territory, ', ' ) WITHIN GROUP ( ORDER BY t.territory ) AS territories
  FROM   ( SELECT ROWNUM AS rn, COLUMN_VALUE AS territory FROM TABLE( territories ) ) t
         INNER JOIN
         ( SELECT ROWNUM AS rn, COLUMN_VALUE AS format FROM TABLE( formats ) ) f
         ON ( f.rn = t.rn )
  GROUP BY f.format;
END;
/

PRINT :cur;

输出日期格式及对应该格式的领土列表:

FORMAT       TERRITORIES
------------ ------------------------------------------------------------------
DD MON RRRR  THAILAND
DD-MM-RR     ALGERIA, BAHRAIN, INDIA, MOROCCO, THE NETHERLANDS, TUNISIA
DD-MM-RRRR   BANGLADESH, INDONESIA, ROMANIA, VIETNAM
DD-MON-RR    AMERICA, CHINA, HONG KONG, IRELAND, ITALY, PAKISTAN, TAIWAN,
             UNITED KINGDOM
DD-MON-RRRR  ISRAEL
DD.MM.RR     AUSTRIA, BELARUS, CIS, CROATIA, CZECH REPUBLIC, CZECHOSLOVAKIA,
             GERMANY, RUSSIA, SLOVAKIA, SLOVENIA, SWITZERLAND
DD.MM.RRRR   ALBANIA, AZERBAIJAN, ESTONIA, FINLAND, FYR MACEDONIA, ICELAND,
             KAZAKHSTAN, MACEDONIA, NORWAY, SERBIA AND MONTENEGRO, UKRAINE,
             YUGOSLAVIA
DD.MM.RRRR.  MONTENEGRO, SERBIA
DD.fmMM.RRRR ARMENIA
DD/MM/RR     AFGHANISTAN, BELGIUM, BRAZIL, CAMEROON, CATALONIA, CHILE, COLOMBIA,
             CONGO BRAZZAVILLE, CONGO KINSHASA, COSTA RICA, CYPRUS, DJIBOUTI,
             EGYPT, EL SALVADOR, FRANCE, GABON, GREECE, GUATEMALA, HONDURAS,
             IRAQ, IVORY COAST, JORDAN, KUWAIT, LEBANON, LIBYA, LUXEMBOURG,
             MAURITANIA, MEXICO, NEW ZEALAND, NICARAGUA, OMAN, PANAMA, PERU,
             PUERTO RICO, QATAR, SAUDI ARABIA, SINGAPORE, SOMALIA, SPAIN, SUDAN,
             SYRIA, UNITED ARAB EMIRATES, URUGUAY, VENEZUELA, YEMEN
DD/MM/RRRR   ARGENTINA, BAHAMAS, BERMUDA, ECUADOR, MALAYSIA, SENEGAL, TURKEY,
             UGANDA, ZAMBIA
DD/MON/RR    AUSTRALIA, SOUTH AFRICA, UZBEKISTAN
DD/fmMM/RRRR LAOS, NIGERIA
MM/DD/RRRR   PHILIPPINES
RR-MM-DD     CANADA, DENMARK, JAPAN
RR-MON-DD    HUNGARY
RR.MM.DD     PORTUGAL
RR/MM/DD     KOREA, POLAND
RRRR-MM-DD   BULGARIA, SWEDEN
RRRR-fmMM-DD CAMBODIA
RRRR.MM.DD   LATVIA, LITHUANIA
RRRR/fmMM/fm IRAN, SRI LANKA
fmDD-MM-RR   BOLIVIA
fmDD/MM/RR   PARAGUAY
fmDD/MM/RRRR BELIZE, ETHIOPIA, MALTA, NEPAL
fmDD/fmMM/RR MALDIVES
fmMM.DD.RRRR BOSNIA AND HERZEGOVINA
fmMM/DD/RRRR KENYA, TANZANIA

13

Oracle以及其他数据库都允许您设置默认格式。开箱即用的格式通常是DD-MON-RR,其中“RR”代表两位数的年份。从歧义的角度(两位数年份?)和国际化的角度来看,这是一种相当糟糕的格式(那到底是哪些国家的默认格式呢?)。但是Oracle已经存在很长时间了。

ISO(国际标准化组织)也定义了标准格式。他们采用了更像YYYY-MM-DD的格式。实际上,连字符是可选的,但我认为它们使日期更易读。

如果您使用DATE,Oracle接受此格式的常量:

select DATE '2018-01-25'

这非常方便。首先,支持合理的标准很好。其次,不受国际化设置的影响,代码是安全的。当然,Oracle文档详细介绍了这一点;这里是一个开始的地方。


3
谢谢 - 这是我找到的第一个易懂的解释! - BigRedEO
默认情况下,格式永远不是 DD-MON-YY。在某些地区,它是 DD-MON-RR,但在其他地区,它可能完全不同。 - MT0
1
DD-MON-YY 对于在多个语言环境下运行的脚本也是不好的。使用 TO_DATE('01-JAN-01', 'DD-MON-YY') 的脚本只能在英语环境中工作,而使用 TO_DATE('01-ENE-01', 'DD-MON-YY') 的脚本只能在西班牙语环境中工作。 - JustinCB

10

日期就是日期,其格式决定了当您查询数据时日期将如何显示。

如果您没有提供格式并使用 TO_CHAR 函数,则我们将以默认的 NLS_DATE_FORMAT 返回日期,该格式在数据库中定义,但也可以为您的会话指定。

对于您的会话 -

select * from NLS_SESSION_PARAMETERS
where PARAMETER = 'NLS_DATE_FORMAT';

我的日期格式是“DD-MON-YYYY”,因此当我查询SYSDATE时:
SQL> select sysdate from dual;

SYSDATE    
-----------
03-MAY-2018

在处理日期时,通常最好的做法是不要假定特定的日期格式。因此,在插入或选择日期数据时,要明确指出日期格式。例如:

SQL> drop table JUST_DATES;

Table JUST_DATES dropped.

SQL> 
SQL> create table JUST_DATES (
  2      DATE1 date
  3  );

Table JUST_DATES created.

SQL> 
SQL> insert   into JUST_DATES values ( to_date('01-01-2018','MM-DD-YYYY') );

1 row inserted.

SQL> 
SQL> select to_char(
  2      DATE1,
  3      'MON/DD/RR'
  4  )
  5    from JUST_DATES;

TO_CHAR(DATE1,'MON
------------------
JAN/01/18

现在,回答你的问题 - 默认值是什么?这取决于不同情况。

它源自于你的NLS_TERRITORY (文档)

但是,如果你记得最佳实践,并且不假设默认值,以及在使用日期时明确指定日期格式,那么就没问题了。

我们为此主题提供的最佳指南是我们的全球化支持指南。 这是您感兴趣的部分。


0

在使用 TO_DATE插入日期时唯一需要注意的事情是如何解析它。

例子:
不起作用的示例
to_date('31-03-2016','dd-mon-yyyy')

起作用的示例
to_date('31-03-2016','dd-mm-yyyy') to_date('31-Mar-2016','dd-Mon-yyyy')

你传递给 to_date 函数的日期和格式应该匹配。
31 - dd
Mar - Mon
2016 - yyyy


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接