php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #62327 Calculation error in DateTime::add() or DateTime::diff()
Submitted: 2012-06-14 19:04 UTC Modified: 2021-10-14 13:08 UTC
Votes:2
Avg. Score:4.0 ± 1.0
Reproduced:1 of 1 (100.0%)
Same Version:2 (200.0%)
Same OS:2 (200.0%)
From: helmut at qweb dot at Assigned: cmb (profile)
Status: Not a bug Package: Date/time related
PHP Version: Irrelevant OS: Linux, Windows
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: helmut at qweb dot at
New email:
PHP Version: OS:

 

 [2012-06-14 19:04 UTC] helmut at qweb dot at
Description:
------------
The task was to shift a date-interval like [ begin: 2012-05-07, end: 2012-06-01 ] to another begin date like 2012-05-21 - an end date 2012-06-15 is expected (first example). The function shift_datetimeobject() uses DateTime::diff() and DateTime::add() to calculate the end date - it fails in the 3rd of 4 attempts.

The second function shift_timestamp() uses timestamp to solve the same task. it works like expected.

The bug was found on Windows Server 2008/Apache 2.4.2/PHP 5.4.4 and on Debian Linux squeeze/PHP 5.3.3-7+squeeze9 with Suhosin-Patch/cli and libapache2-mod-php5 5.3.3-7+squeeze9

Test script:
---------------
function shift_datetimeobject($begin1, $end1, $begin2) {
    
    $dt_begin1 = new DateTime($begin1);
    $dt_end1 =  new DateTime($end1);

    $span = $dt_begin1->diff($dt_end1);

    $dt_return = new DateTime($begin2);
    return array($dt_return->add($span), $span);
}
function shift_timestamp($begin1, $end1, $begin2) {
    
    $dt_begin1 = new DateTime($begin1);
    $st_begin1 = $dt_begin1->getTimestamp();

    $dt_end1 =  new DateTime($end1);
    $st_end1 = $dt_end1->getTimestamp();

    $span = $st_end1 - $st_begin1;

    $dt_begin2 = new DateTime($begin2);
    $st_begin2 = $dt_begin2->getTimestamp();

    $st_end2 = $st_begin2 + $span;
    $dt_end2 = new DateTime();
    return $dt_end2->setTimestamp($st_end2);   
}

$params = array(
    array('2012-05-07', '2012-06-01', '2012-05-21'),
    array('2012-06-11', '2012-07-13', '2012-06-18'),
    array('2012-06-25', '2012-07-27', '2012-07-02'),
    array('2012-07-09', '2012-07-27', '2012-07-16'),
);
foreach ($params as $p) {
    $erg_object = shift_datetimeobject($p[0], $p[1], $p[2]);
    $erg_stamp  =      shift_timestamp($p[0], $p[1], $p[2]);    
    printf("Params: %s, %s, %s\n", $p[0], $p[1], $p[2]);
    printf('$erg_stamp     = %s' . "\n", $erg_stamp->format('Y-m-d'));
    printf('$erg_object[0] = %s' . "\n", $erg_object[0]->format('Y-m-d'));
    printf("span (object)  = %s\n", $erg_object[1]->format('%a days %H:%I:%S'));
    if ($erg_object[0] == $erg_stamp) {
        echo "OK";
    } else {
        echo "WRONG!";
    }
    echo "\n\n";
}

Expected result:
----------------
Params: 2012-05-07, 2012-06-01, 2012-05-21
Result: 2012-06-15

Params: 2012-06-11, 2012-07-13, 2012-06-18
Result: 2012-07-20

Params: 2012-06-25, 2012-07-27, 2012-07-02
Result: 2012-08-03

Params: 2012-07-09, 2012-07-27, 2012-07-16
Result: 2012-08-03

Actual result:
--------------
Params: 2012-05-07, 2012-06-01, 2012-05-21
$erg_stamp     = 2012-06-15
$erg_object[0] = 2012-06-15
span (object)  = 25 days 00:00:00
OK

Params: 2012-06-11, 2012-07-13, 2012-06-18
$erg_stamp     = 2012-07-20
$erg_object[0] = 2012-07-20
span (object)  = 32 days 00:00:00
OK

Params: 2012-06-25, 2012-07-27, 2012-07-02
$erg_stamp     = 2012-08-03
$erg_object[0] = 2012-08-04
span (object)  = 32 days 00:00:00
WRONG!

Params: 2012-07-09, 2012-07-27, 2012-07-16
$erg_stamp     = 2012-08-03
$erg_object[0] = 2012-08-03
span (object)  = 18 days 00:00:00
OK

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-07-09 13:54 UTC] xKhorasan+php at gmail dot com
Submitted a pull request to derickr/timelib.
https://github.com/derickr/timelib/pull/15
 [2021-07-27 03:52 UTC] antonino dot spampinato86 at gmail dot com
Is the expected result, diff is likely to be unique for two dates. The php arithmetic is not one to one like the human one, in fact a month can be 30 days, 31, 28 or 29 if leap year. I show you the raw code, where I have voluntarily created and overwritten the variable $interval, if you try to comment on the second one will get an incorrect result but this is because the calculation is not one to one but as I explained above (diff is almost always unique ).

https://3v4l.org/MKeKE
This not a bug :)
 [2021-10-14 13:08 UTC] cmb@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: cmb
 [2021-10-14 13:08 UTC] cmb@php.net
Indeed, this is not a bug, because a DateInterval is not
necessarily a time span in seconds.

> Params: 2012-06-25, 2012-07-27, 2012-07-02
> Result: 2012-08-03

The difference between the first two dates is 1 month and 2 days.
Adding that to the third date gives 2012-07-04.  See
<https://3v4l.org/7d0dn>.
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Sat Oct 16 01:03:34 2021 UTC