【问题标题】:'DD-MON-RR' date format pattern not working as expected“DD-MON-RR”日期格式模式未按预期工作
【发布时间】:2014-03-14 07:35:01
【问题描述】:

我正在插入格式为

的查询
to_date('25-JUN-13','DD-MON-RR')

oracle 中工作正常,输出为 25-JUN-13
postgresql 中与 0001-06-25 BC 相同。

这是一个从oracle数据库到postgresql迁移项目。任何解决方案都可以像 oracle 一样工作。

如果我使用 DD-MM-YY 格式,同样无法正常工作,那么结果会大不相同。

在 POSTGRESQL 中运行这个查询 -->

select to_char(to_date('25-JUN-53','DD-MON-YY'),'YYYY') as YEAR 

ANSWER IS --> 2053 

在 oracle 中从查询中检索与

相同的结果时
select to_char(to_date('25-JUN-53','DD-MON-RR'),'YYYY') as YEAR from dual

ASSWER IS --> 1953

在我迁移项目时,Postgresql 中应该有相同的功能,因此最终结果应该相同。

【问题讨论】:

  • 你想得到年啊?
  • 为什么你只给年份2位数
  • 我只在这种格式的表中检索值。

标签: oracle postgresql datetime types postgresql-9.1


【解决方案1】:

同样的工作不正常......

当然它工作正常。 The manual:

如果年份格式规范少于四位,例如YYY, 并且提供的年份小于四位数,年份将是 调整为最接近年份 2020,例如95 变为 1995。

所以,70 变为 1970,但 69 变为 2069。

Oracle 对格式说明符RR 有不同的规则,Postgres 中不存在这种规则。基本上,年份会被调整到最接近年份2000(离当前日期最近的世纪):

解决方法

将功能封装在一个根据字符串中的年份编号切换世纪的函数中。由于 Postgres 允许函数重载,您甚至可以使用相同的函数名称 to_date() 和不同的参数类型。见:

根据上面的文档,Oracle 在 YY = '50' 处回绕,这个函数在 2049 年之前是等效的:

CREATE OR REPLACE FUNCTION to_date(varchar, text)
  RETURNS date
  LANGUAGE sql STABLE AS
$func$
SELECT CASE WHEN right($1, 2) > '49' THEN
         to_date(left($1, -2) || '19' || right($1, 2), 'DD-MON-YYYY')
      ELSE
         to_date(left($1, -2) || '20' || right($1, 2), 'DD-MON-YYYY')
      END
$func$;

只有STABLE,而不是IMMUTABLE,因为to_date 只是STABLE。否则你禁用函数内联。

我选择varchar作为第一个参数与原来的不同,它使用text
如果年份数字 > 49,则函数将 20 世纪(带有 '19')添加到转换前的日期字符串中,否则将 21 日添加到日期字符串中。第二个参数被忽略

呼叫:

SELECT to_date('25-JUN-53'         , 'DD-MON-YY') AS original
     , to_date('25-JUN-53'::varchar, 'DD-MON-YY') AS patched1
     , to_date('25-JUN-53'::varchar, 'DD-MON-RR') AS patched2
     , to_date('25-JUN-53'::varchar, 'FOO-BAR')   AS patched3

我们的自定义函数无论如何都会忽略第二个参数。

结果:

  original  |  patched1  |  patched2  |  patched3  
------------+------------+------------+------------
 2053-06-25 | 1953-06-25 | 1953-06-25 | 1953-06-25

db小提琴here
sqlddle

您可能会使其在 2049 之后工作更复杂,并考虑第二个参数...


警告:在基本函数之上的函数重载最好小心。如果它留在你的系统中,以后可能会有人得到令人惊讶的结果。

最好在特殊模式中创建该函数并有选择地设置search_path,以便仅在适当时使用它。在这种情况下,您也可以使用text 作为参数类型:

CREATE SCHEMA specialfunc;
CREATE OR REPLACE FUNCTION specialfunc.to_date(text, text) AS ...

然后:

SET search_path = specialfunc, pg_catalog;
SELECT to_date('25-JUN-53', 'DD-MON-YY') AS patched;

使用临时功能。见:

【讨论】:

  • 对不起“同样的工作不正常”我只是要求相同的功能 dd-mon-rr 格式(如果在 postgres 中也可用)?类似于 orale dd-mon-rr 格式
  • 不错的解决方案!我的条件很好
  • RR 很混乱
【解决方案2】:

这是我为解决此问题而编写的示例函数。我自己也在做同样的迁移。希望它以某种方式帮助你。您可以使用函数重载来实现,只需根据需要重命名函数即可。

CREATE OR REPLACE FUNCTION to_date_rr(TEXT, TEXT)
  RETURNS DATE AS
  $$
      DECLARE
        date_v DATE;
        fmt text := upper($2);
        DATE_VALUE TEXT :=$1;
        digit_diff numeric := length($1) - length($2);

      BEGIN

    $2 = upper($2);

    IF substring(fmt from position('RRRR' in fmt) for 4) = 'RRRR' THEN

        IF digit_diff < 0 THEN
            fmt := replace($2, 'RRRR', 'YYYY');
            IF substring(DATE_VALUE from position('RRRR' in $2) for 2) > '50' THEN
                date_v := to_date(overlay(DATE_VALUE placing '19' from position('RRRR' in $2) for 0), fmt);
            ELSE
                date_v := to_date(overlay(DATE_VALUE placing '20' from position('RRRR' in $2) for 0), fmt);
            END IF;
        ELSE
            fmt := replace($2, 'RRRR', 'YYYY');
            date_v := to_date($1, fmt);
        END IF;

    ELSIF substring(fmt from position('RR' in fmt) for 2) = 'RR' THEN
        IF digit_diff = 0 THEN
            fmt := replace($2, 'RR', 'YY');         
            IF substring(DATE_VALUE from position('RR' in $2) for 2) > '50' THEN
                date_v := to_date(overlay(DATE_VALUE placing '19' from position('RR' in $2) for 0), fmt);
            ELSE
                date_v := to_date(overlay(DATE_VALUE placing '20' from position('RR' in $2) for 0), fmt);
            END IF;


        ELSE
            fmt := replace($2, 'RR', 'YY');
            date_v := to_date($1, fmt);

        END IF;


    ELSIF substring(fmt from position('YY' in fmt) for 2) = 'YY' and substring(fmt from position('YYYY' in fmt) for 4) != 'YYYY' THEN
        IF digit_diff = 0 THEN  
            IF substring(DATE_VALUE from position('YY' in $2) for 2) >= '00' THEN
                date_v := to_date(overlay(DATE_VALUE placing '20' from position('YY' in $2) for 0), fmt);

            END IF;

        ELSIF digit_diff < 0 THEN
            IF substring(DATE_VALUE from position('YY' in $2) for 2) >= '00' THEN
                date_v := to_date(overlay(DATE_VALUE placing '200' from position('YY' in $2) for 0), fmt);      
            END IF;
        ELSE
            date_v := to_date($1, fmt);
        END IF;

    ELSE
        SELECT to_date($1, $2) INTO date_v;

    END IF;


        RETURN date_v;

      END;
  $$
LANGUAGE plpgsql;

【讨论】:

    【解决方案3】:

    可以使用 to_char(Date Field, 'format to display')

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多