【问题标题】:2020-02-29 minus one year caused error ORA-01839: date not valid for month specified2020-02-29 减去一年导致错误 ORA-01839:指定月份的日期无效
【发布时间】:2020-08-01 19:54:53
【问题描述】:

我有一个简单的查询:

select to_date('2020-02-29', 'yyyy-mm-dd') - interval '1' year from dual

我认为结果应该是2019-02-28,但是oracle抛出错误为:

错误报告 -
ORA-01839: 指定月份的日期无效

【问题讨论】:

  • 搞笑,我觉得应该是2019-03-01.. 也许这就是为什么会出现错误
  • @CaiusJard:FWIW、Postgres、SQL Server、DB2、MySQL 和 Java 确实返回 2019-02-28
  • 我一直认为3月32日和4月1日是一回事;显然,这些系统的开发人员按照不同的逻辑工作!

标签: sql oracle


【解决方案1】:

那是the documented behaviour;它甚至举了一个例子:

当间隔计算返回一个日期时间值时,结果必须是 实际的日期时间值或数据库返回错误。为了 例如,接下来的两个语句返回错误:

SELECT TO_DATE('31-AUG-2004','DD-MON-YYYY') + TO_YMINTERVAL('0-1')
  FROM DUAL;

SELECT TO_DATE('29-FEB-2004','DD-MON-YYYY') + TO_YMINTERVAL('1-0')
  FROM DUAL;

第一个失败是因为在 31 天的月份中加上一个月会 结果为 9 月 31 日,这是无效的日期。第二次失败 因为将一年添加到仅每四年存在一次的日期是 无效。但是,下一条语句成功了,因为添加了四个 年到 2 月 29 日有效:

SELECT TO_DATE('29-FEB-2004', 'DD-MON-YYYY') + TO_YMINTERVAL('4-0')
  FROM DUAL;

TO_DATE('
---------
29-FEB-08

替代方法是使用add_months(..., -12) (docs),不会出错:

select add_months(date '2020-02-29', -12) from dual;

ADD_MONTHS
----------
2019-02-28

但请注意,它是如何处理一个月中不同天数的;当你正好回到一年时,这并不是一个真正的问题,但仍然需要注意:

如果 date 是该月的最后一天,或者如果结果月份的天数少于 date 的天数,则结果是该月的最后一天结果月份。

所以其中一些可能没有达到你的预期:

with rcte (dt) as (
  select last_day(date '2020-01-01')
  from dual
  union all
  select last_day(trunc(dt, 'MM') + interval '1' month)
  from rcte
  where dt < date '2020-06-01'
)
select dt,
  add_months(dt, -12) as minus12, add_months(dt, -3) as minus3, add_months(dt, -1) as minus1,
  add_months(dt, 1) as plus1, add_months(dt, 3) as plus3, add_months(dt, 12) as plus12
from rcte
order by dt;

DT         MINUS12    MINUS3     MINUS1     PLUS1      PLUS3      PLUS12    
---------- ---------- ---------- ---------- ---------- ---------- ----------
2020-01-31 2019-01-31 2019-10-31 2019-12-31 2020-02-29 2020-04-30 2021-01-31
2020-02-29 2019-02-28 2019-11-30 2020-01-31 2020-03-31 2020-05-31 2021-02-28
2020-03-31 2019-03-31 2019-12-31 2020-02-29 2020-04-30 2020-06-30 2021-03-31
2020-04-30 2019-04-30 2020-01-31 2020-03-31 2020-05-31 2020-07-31 2021-04-30
2020-05-31 2019-05-31 2020-02-29 2020-04-30 2020-06-30 2020-08-31 2021-05-31
2020-06-30 2019-06-30 2020-03-31 2020-05-31 2020-07-31 2020-09-30 2021-06-30

【讨论】:

  • 谢谢@alex-poole。不知道为什么oracle没有解决这个间隔问题。
猜你喜欢
  • 2016-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多