I'm looking for standards for Date/Time addition. I haven't been able to find any. In particular I'm hoping to find a spec that defines what should happen when you add a month to a date like January 31st. Is the right answer February 28th(/29th)? March 1st? March 2nd?
I've seen inconsistent implementations between different tools (PHP & MySQL in this case), and I'm trying to find some sort of standards to base my work on.
Differing Results:
PHP
$end = strtotime("+1 month", 1314835200);
//1317513600 Sat, 01 Oct 2011 20:00:00 -0400
MySQL
SELECT UNIX_TIMESTAMP(DATE_ADD(FROM_UNIXTIME(1314835200), INTERVAL 1 MONTH));
#1317427200 Fri, 30 Sep 2011 20:00:00 -0400
Oracle
SELECT ADD_MONTHS('31-Aug-11', 1) FROM dual;
#30-SEP-11
(sorry for the format change, my oracle foo is weak)
Java
Calendar c = Calendar.getInstance();
c.clear();
c.set( 2011, Calendar.AUGUST, 31 );
c.add( Calendar.MONTH, 1 );
c.getTime()
#Fri Sep 30 00:00:00 EDT 2011
Try the mysql date function :
SELECT ADDDATE('2011-01-31', INTERVAL 1 MONTH) // 2011-02-28
Input date with leap year
SELECT ADDDATE('2012-01-31', INTERVAL 1 MONTH) // 2012-02-29
In the .NET framework the behavior of System.DateTime.AddMonths is as follows:
I've tested how it works exactly:
I believe the defacto standard is ISO 8601. Unfortunately, there are many ambiguities, for example:
Date arithmetic is not defined
Addition is not commutative or associative
Subtraction is not the inverse of Addition.
Precision of decimal fractions can vary.
The full specification can be found at http://www.iso.org/iso/catalogue_detail.htm?csnumber=26780
I think each product is attempting to adhere to an impossible to implement standard. The ambiguous parts are open to interpretation and so everyone interprets. This is the same standard that opened us up to the Y2K bug!!
Myself, I favor an implementation that converts a date/time to a 1970 based number (UNIX timestamp), performs the calculation and converts back. I believe this is the approach taken by Oracle/MySQL. I am surprised that more attention has not been paid this issue, as it is really important, sometimes critical, in so many applications. Thanks for the question!
Edit: While doing some more reading, I found Joe Celko's thoughts on different date/time representations and standardization HERE.
Query:
Output:
My conclusion:
source
Problem here is that it doesn't mention that the month is actually the month from the input date.
Joda-Time in Java chooses the previous valid date when an invalid one is created. For example,
2011-01-31 + P1M = 2011-02-28
. I believe this is the most widely chosen default choice in date-time libraries, and thus a de facto standard.ThreeTen/JSR-310 provides a strategy pattern for this, with four choices, see the code.
More amusing is the question of what the answer to
2011-01-31 + P1M-1D
is. If you add the month, then resolve the invalid date, then subtract the day, you get 2011-02-27. But I think most users expect 2011-02-28 because the period is being added in a single lump. See how ThreeTen handles this here.I have considered trying to write a general purpose best practices in date/time calculations, or actual spec, but haven't really had the time!
First day of the month + 1 month should equal the first of the next month. Trying this on SQL Server
This results in
Last day of this month + 1 month should equal last day of next month. This should go for next month, current month, 10 months down, etc.
This results in
See how 31, 30, 29 all become feb 29 (2012 is a leap year).
p.s. I took off the time parts (all zeroes) to help make it more readable