ZF-4599: Zend_Date::isDate() and Zend_Validate_Date do not reliably check format

Description

Neither Zend_Date::isDate() or Zend_Validate_Date can be used to reliably check the date for format AND existence. Take the following unit test and it's results as an example.

Below, we expect all dates to be in this format: 2008-01-01 and be a date that exists on the calendar:

 
class BPDateValidatorTest extends PHPUnit_Framework_TestCase
{

    /**
     * @var Zend_Validate_Date
     */
    private $_oValidator;

    public function setUp()
    {
        $this->_oValidator = new Zend_Validate_Date( 'YYYY-MM-DD' );
    }
    public function testValidDatesSHouldNotFail1()
    {
        $sDate = date( 'Y-m-d' );

        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertTrue( $bResult, $sDate . ' did not validate correctly: ' . $this->_oValidator->getMessages() );
    }

    public function testValidDatesSHouldNotFail2()
    {
        $sDate = '1984-03-08';
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertTrue( $bResult, $sDate . ' did not validate correctly: ' . $this->_oValidator->getMessages() );
    }

    public function testInvalidDateFormatShouldBeInvalid1()
    {
        $sDate = 'fooo-ba-rs';
        $bResult = Zend_Date::isDate( $sDate, 'yyyy-MM-dd' );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }

    public function testInvalidDateFormatShouldBeInvalid2()
    {
        $sDate = date( 'Y/m/d' );
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }


    public function testInvalidDateFormatShouldBeInvalid3()
    {
        $sDate = date( 'r' );
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }


    public function testInvalidDateFormatShouldBeInvalid4()
    {
        $sDate = date( 'Y:m:d' );
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }

    public function testInvalidDateFormatShouldBeInvalid5()
    {
        $sDate = date( 'Y-m-d H:i' );
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }

    public function testLeapYearFebruaryWith29DaysShouldPass1()
    {
        $sDate = '2008-02-29';
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertTrue( $bResult, $sDate . ' did not validate correctly' );
    }

    public function testNonLeapYearFebruaryWith29DaysShouldBeInvalid1()
    {
        $sDate = '2009-02-29';
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }

    /**
     * This handles the fact that 1900 and 2100 are NOT leap years.  Since
     * The validator will not acceppt a date after 2099, we don't have to
     * worry about that date, but 1900-04-29 shouldn't validate.
     *
     */
    public function testNonLeapYearFebruaryWith29DaysShouldBeInvalid2()
    {
        $sDate = '1900-02-29';
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }

    public function testFebruaryWith30DaysShouldBeInvalid()
    {
        $sDate = '2008-02-30';
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }

    public function testShortMonthsWith31DaysShouldBeInvalid()
    {
        $sDate = '2008-09-31';
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertFalse( $bResult, $sDate . ' validated correctly' );
    }

    public function testLongMonthsWith31DaysShouldPass()
    {
        $sDate = '2008-10-31';
        $bResult = $this->_oValidator->isValid( $sDate );
        $this->assertTrue( $bResult, $sDate . ' did not validate correctly' );
    }

}
 
-----------------------------------------------------
TEST RESULTS:
-----------------------------------------------------

 Test Valid Dates S Hould Not Fail1: SUCCESS
 Test Valid Dates S Hould Not Fail2: SUCCESS
 Test Invalid Date Format Should Be Invalid1: SUCCESS
 Test Invalid Date Format Should Be Invalid2: FAILURE
 Test Invalid Date Format Should Be Invalid3: SUCCESS
 Test Invalid Date Format Should Be Invalid4: FAILURE
 Test Invalid Date Format Should Be Invalid5: FAILURE
 Test Leap Year February With29 Days Should Pass1: SUCCESS
 Test Non Leap Year February With29 Days Should Be Invalid1: SUCCESS
 Test Non Leap Year February With29 Days Should Be Invalid2: SUCCESS
 Test February With30 Days Should Be Invalid: SUCCESS
 Test Short Months With31 Days Should Be Invalid: SUCCESS
 Test Long Months With31 Days Should Pass: SUCCESS

notice that the following formats are actually validating correctly, despite me strictly specifying the format:

date( 'Y/m/d' ); date( 'Y:m:d' ); date( 'Y-m-d H:i' );

When using Zend_Date::isDate(), I get similar results except with date( 'r' ) passing validation as well.

Comments

Your testInvalidDateFormatShouldBeInvalid2, 4 and 5 does not test properly.

Why should a Y.m.d where . is the separator not be valid ? You are testing for the date and not if the string contains this separators.

Separators are independently from the date and you can use any separator as long as the date is correct.

Closed due to no response

I apologize. When I went to respond, I did not have sufficient rights to leave a comment for some reason. This was fixed a few days ago, and I since forgot.

When a format is passed in, you would expect the date to validate the format as well. Seperator characters fall into the category "expected format" as loosely described:

http://framework.zend.com/manual/en/… "And if the optional format option is set this format is used for the validation. "

AND

http://framework.zend.com/manual/en/… "The second parameter can be the format which the date is expected to have."

Either the functionality should be changed to match the description, or the documentation should be ammended.

En Hvit Vulkaniseret only supports skoen http://www.nikeshoxsko.biz under and avsluttes with a brun gummi under føttene. Each par will completely with a sett av Kork innleggssåler, extra Blonder and Stov bags.More detaljerte Pictures of "Woodgrain" Janoski below. You can http://www.nikeshoxsko.biz plukke opp et par achieve with forhandlere as Primitive.