简体   繁体   English

2020-02-29 减去一年导致错误 ORA-01839:指定的月份日期无效

[英]2020-02-29 minus one year caused error ORA-01839: date not valid for month specified

I have a simple query as:我有一个简单的查询:

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

I think the result should be 2019-02-28 , but oracle throws error as:我认为结果应该是2019-02-28 ,但 oracle 抛出错误为:

Error report -错误报告 -
ORA-01839: date not valid for month specified ORA-01839: 日期对于指定的月份无效

That is the documented behaviour ;那是记录在案的行为 it even gives this as an example:它甚至以此为例:

When interval calculations return a datetime value, the result must be an actual datetime value or the database returns an error.当间隔计算返回日期时间值时,结果必须是实际的日期时间值,否则数据库将返回错误。 For example, the next two statements return errors:例如,接下来的两个语句返回错误:

 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;

The first fails because adding one month to a 31-day month would result in September 31, which is not a valid date.第一个失败,因为将一个月添加到 31 天的月份将导致 9 月 31 日,这不是一个有效日期。 The second fails because adding one year to a date that exists only every four years is not valid.第二个失败,因为将一年添加到仅每四年存在的日期是无效的。 However, the next statement succeeds, because adding four years to a February 29 date is valid:但是,下一条语句成功了,因为将 4 年添加到 2 月 29 日日期是有效的:

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

The alternative is to use add_months(..., -12) ( docs ), which won't error:另一种方法是使用add_months(..., -12) ( docs ),它不会出错:

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

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

But note how that deals with different number of days in the month;但请注意它是如何处理一个月中不同天数的; not really an issue when you're going back exactly a year, but still something to be aware of:当您返回整整一年时并不是真正的问题,但仍然需要注意:

If date is the last day of the month or if the resulting month has fewer days than the day component of date , then the result is the last day of the resulting month.如果date是该月的最后一天,或者如果结果月份的天数少于date 的日期部分,则结果是结果月份的最后一天。

So some of these might not do what you expected:因此,其中一些可能不会达到您的预期:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM