Oracle even caters for a drift noted in the 8th century fixed in the 16th century - due to some issues with the dates chosen for Easter by the catholic church.
Here I add a month to the last day of August:
Likewise, the documentation states:
The same error will occur however if you attempt to subtract from a month such as July.
Perhaps this behaviour with intervals will be modified in a future release?
select date '1582-10-04' + 1 gregory from dual;Let's take for instance one method of adding one month to a given day.
GREGORY
-------------------
15-10-1582 00:00:00
Here I add a month to the last day of August:
select add_months(last_day(date '2009-08-01'), 1) end_of_sept from dual;And I safely get the last day of September (which has one less day).
END_OF_SEPT
-------------------
30-09-2009 00:00:00
Likewise, the documentation states:
For example, the MONTHS_BETWEEN
function returns the number of months between two dates. The fractional portion of the result represents that portion of a 31-day month.
So these three expressions will return slightly different resultsselect months_between(last_day(date '2008-02-01'), (date '2008-02-01')) is_28_daysHowever if you're working with intervals here is a little trap to watch out for.
,months_between(last_day(date '2009-08-01'), (date '2009-08-01')) is_30_days
,months_between(last_day(date '2009-09-01'), (date '2009-09-01')) is_29_days
from dual;
IS_28_DAYS IS_30_DAYS IS_29_DAYS
---------- ---------- ----------
.903225806 .967741935 .935483871
select last_day(date '2009-08-01')Interestingly if you subtract one month interval from the end of September you get 30th August. Similar behaviour occurs when subtracting from a leap February. It seems an interval of one month is considered as 31 days, but it can't allow properly for smaller months.
+ INTERVAL '1' MONTH end_of_sept
from dual;
+ INTERVAL '1' MONTH int
*
ERROR at line 2:
ORA-01839: date not valid for month specified
The same error will occur however if you attempt to subtract from a month such as July.
select last_day(date '2009-09-01')The 11g documentation states:
- INTERVAL '1' MONTH end_of_aug
from dual;
END_OF_AUG
-------------------
30-08-2009 00:00:00
When interval calculations return a datetime value, the result must be an actual datetime value or the database returns an error...Personally I can't quite grasp why there should be a difference between interval arithmetic and functions such asSELECT TO_DATE('31-AUG-2004','DD-MON-YYYY') + TO_YMINTERVAL('0-1') 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.
months_between
, but just be aware if you need to be pedantic with your dates.Perhaps this behaviour with intervals will be modified in a future release?
As I understand it, INTERVAL is part of the SQL standard so oracle are following the rules. MONTHS_BETWEEN is Oracle specific, so they can make it work however they want.
ReplyDeletePersonally, I like that INTERVAL fails rather than giving debatable responses. I've never been happy that adding one month to Feb 28th gives March 31st.
Well that makes sense. I think it's good we have the option. I guess it would depend on how you need your application to behave - and the example you gave is a good one.
ReplyDelete