ZF-3300: Wrong comparison operator used in Zend_Date addMillisecond method results in an inaccurate timestamp

Description

If the current millisecond precision of the Zend_Date class is set to 3 the local $max variable will evaluate to 1000, but you will need to add 1001 millisecond to get it to add a whole second to the timestamp instead of 1000.

Instead of the $this->_Fractional > $max condition, it should be changed to $this->_Fractional >= $max

Between lines 4420 and 4427 of Zend/Date.php

    $max = pow(10, $this->_Precision);
    // milli includes seconds
    if ($this->_Fractional > $max) {
        while ($this->_Fractional > $max) {
            $this->addSecond(1);
            $this->_Fractional -= $max;
        }
    }

should be

    $max = pow(10, $this->_Precision);
    // milli includes seconds
    if ($this->_Fractional >= $max) {
        while ($this->_Fractional >= $max) {
            $this->addSecond(1);
            $this->_Fractional -= $max;
        }
    }

Comments

Please give example code for reproduction

Quick sample code and you can see the improper seconds. Changing the (>) to (>=) fixes the issue


<?php

set_include_path(get_include_path() . PATH_SEPARATOR . '../library/Zend');

require_once 'Zend/Date.php';

$date = new Zend_Date();


// pick a timestamp any time stamp will do
$date->setTimestamp(1234567890);
// Have a look at the date and note how many seconds
echo "Date set: " . $date->getTime() . "
\n"; // Zend_Date defaults to 0 milliseconds echo "Milliseconds: " . $date->getMillisecond() . "
\n"; // Get the current millisecond precision echo "Millisecond Precison: " . $date->getFractionalPrecision() . "
\n"; // By default Zend_Date has 3 digit precison for milliseconds // which would mean 1000 = 1 second so lets add 1000 and check the date $date->addMilliSecond(1000); echo "New Date set: " . $date->getTime() . "
\n"; // Zend_Date defaults to 0 milliseconds echo "New Milliseconds: " . $date->getMillisecond() . "
\n"; // Oops! we have 1000 milliseconds but havent added a new second to the timestamp // lets add 1 more millisecond for a total of 1001 milliseconds $date->addMilliSecond(1); echo "New Date set: " . $date->getTime() . "
\n"; // Zend_Date defaults to 0 milliseconds echo "New Milliseconds: " . $date->getMillisecond() . "
\n"; // Now it rolled over and added a new second ?>

Please forgive the extra comment lines and typos. First bug in a major project I've been the first to find and fix :)

typo fix...

precision of the Zend_Date class is set to 2 changed to precision of the Zend_Date class is set to 3

since precision of 2 would evaluate to 100 not 1000

Fixed with r9523.

Thanks for the example code and the outlined patch.

Updating for the 1.6.0 release.