PHP :: Bug #49081 :: [PATCH] DateTime::diff() mistake if start in January and interval
| Bug #49081 | [PATCH] DateTime::diff() mistake if start in January and interval > 28 days | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Submitted: | 2009-07-27 22:55 UTC | Modified: | 2010-05-04 17:12 UTC |
|
||||||||||
| From: | nate at frickenate dot com | Assigned: | derick (profile) | |||||||||||
| Status: | Closed | Package: | Date/time related | |||||||||||
| PHP Version: | 5.3.0 | OS: | * | |||||||||||
| Private report: | No | CVE-ID: | None | |||||||||||
[2009-07-27 22:55 UTC] nate at frickenate dot com
Description:
------------
DateTime::diff calculates diffs incorrectly.
As an example, the diff of 2009-01-01 and 2009-03-31 *should be* "2 months, 30 days". 2009-01-01 + 2 months = 2009-03-01, + 30 days = 2009-03-31. Taking 2009-01-01 and using DateTime::add() to add 'P2M30D' does indeed result in 2009-03-31. This is correct.
However, running the diff() of 2009-01-01 and 2009-03-31 returns "3 months, 2 days". add()ing 2009-01-01 + 'P3M2D' returns 2009-04-03 instead of 2009-03-31 (as it should, since the diff that told us to add 3M2D was incorrect).
Reproduce code:
---------------
<?
$jan_1 = new DateTime('2009-01-01');
$mar_31 = new DateTime('2009-03-31');
var_dump(date_diff($jan_1, $mar_31));
var_dump($jan_1->add(new DateInterval('P2M30D'))); // correct period
var_dump($jan_1->add(new DateInterval('P3M2D'))); // incorrect period
// END EXAMPLE CODE - following is just extra fluff
// here's the replacement function I am currently
// using to calculate the correct diff until the
// built-in method is patched and functional
function date_diff2 ($t1, $t2) {
if (! preg_match('/^\d+\z/', $t1) && ($t1 = strtotime($t1)) === false)
return false;
if (! preg_match('/^\d+\z/', $t2) && ($t2 = strtotime($t2)) === false)
return false;
if ($t1 > $t2)
list($t1, $t2) = array($t2, $t1);
$diffs = array(
'years' => 0, 'months' => 0, 'days' => 0,
'hours' => 0, 'minutes' => 0, 'seconds' => 0,
);
foreach (array_keys($diffs) as $interval) {
while ($t2 >= ($t3 = strtotime("+1 ${interval}", $t1))) {
$t1 = $t3;
++$diffs[$interval];
}
}
return $diffs;
}
?>
Expected result:
----------------
object(DateInterval)#3 (8) {
["y"]=>
int(0)
["m"]=>
int(2)
["d"]=>
int(30)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
int(89)
}
object(DateTime)#1 (3) {
["date"]=>
string(19) "2009-03-31 00:00:00"
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "America/Montreal"
}
object(DateTime)#1 (3) {
["date"]=>
string(19) "2009-07-03 00:00:00"
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "America/Montreal"
}
Actual result:
--------------
object(DateInterval)#3 (8) {
["y"]=>
int(0)
["m"]=>
int(3)
["d"]=>
int(2)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
int(89)
}
object(DateTime)#1 (3) {
["date"]=>
string(19) "2009-03-31 00:00:00"
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "America/Montreal"
}
object(DateTime)#1 (3) {
["date"]=>
string(19) "2009-07-03 00:00:00"
["timezone_type"]=>
int(3)
["timezone"]=>
string(16) "America/Montreal"
}
Patches
Pull Requests
History
AllCommentsChangesGit/SVN commits
[2009-12-16 05:44 UTC] peter dot schleif at gmx dot de
More simple code to reproduce error: <?php date_default_timezone_set('Europe/Berlin'); $d1 = new DateTime('2010-01-01 06:00:00'); $d2 = new DateTime('2010-01-31 10:00:00'); $d = $d1->diff($d2); print_r($d); ?> Expected: --------- [m] => 0 [d] => 30 Actual: ------- [m] => 1 [d] => 2[2010-01-05 22:11 UTC] danielc@php.net
[2010-01-06 16:19 UTC] danielc@php.net
[2010-01-23 00:10 UTC] danielc@php.net
[2010-05-04 17:12 UTC] derick@php.net
-Status: Assigned +Status: Closed
[2010-05-04 17:12 UTC] derick@php.net