php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79580 date_create_from_format misses leap year
Submitted: 2020-05-10 13:22 UTC Modified: 2021-08-08 12:43 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: mcab at acm dot org Assigned: derick (profile)
Status: Closed Package: Date/time related
PHP Version: 7.2.30 OS: Linux
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: mcab at acm dot org
New email:
PHP Version: OS:

 

 [2020-05-10 13:22 UTC] mcab at acm dot org
Description:
------------
date_create_from_format when format is 'z Y' is a day out when day number is after feb28.


Test script:
---------------
$TZ= timezone_open( "UTC" );
$refdate=date_create_from_format ( "z Y" ,"31 2020" ,$TZ );
echo date_format($refdate,"d/m/Y")."<br>";

$refdate=date_create_from_format ( "z Y" ,"60 2020" ,$TZ );
echo date_format($refdate,"d/m/Y")."<br>";

$refdate=date_create_from_format ( "z Y" ,"91 2020" ,$TZ );
echo date_format($refdate,"d/m/Y")."<br>";

$refdate=date_create_from_format ( "z Y" ,"121 2020" ,$TZ );
echo date_format($refdate,"d/m/Y")."<br>";

$refdate=date_create_from_format ( "z Y" ,"130 2020" ,$TZ );
echo date_format($refdate,"d/m/Y")."<br>";

/*  Output is 
01/02/2020
02/03/2020   <- This has missed the fact that 2020 is a leap year
02/04/2020   <- All future date are wrong
02/05/2020
11/05/2020
*/


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-05-10 13:41 UTC] cmb@php.net
-Status: Open +Status: Verified
 [2020-05-10 13:41 UTC] cmb@php.net
Confirmed: <https://3v4l.org/Drfk6>.
 [2020-05-10 13:45 UTC] rowan dot collins at gmail dot com
It looks like the problem is that the 'z' specifier works by setting "month=1, day=X" (in this case, "the 60th of January"), then normalising the date immediately. But at that point, it doesn't know what year to use for the normalisation, so picks 1970 (which wasn't a leap year).

https://heap.space/xref/php-src/ext/date/lib/parse_date.c?r=9c608bd1#25213

If you reverse the format, you get the expected result, because the correct year has already been selected when the "normalise" call happens:

$TZ=timezone_open( "UTC" );
$refdate=date_create_from_format ( "Y z" ,"2020 60" ,$TZ );
echo date_format($refdate,"d/m/Y");

# 01/03/2020


A possible fix would be to delay the call to timelib_do_normalize until after the whole input has been parsed, including the year specifier.
 [2020-05-10 17:21 UTC] rowan dot collins at gmail dot com
Test and tentative fix here: https://github.com/php/php-src/compare/PHP-7.3...IMSoP:bug-79580 (will actually need a change to timelib, but I'm not sure how to test it there).

This implements the approach in my last comment, but results in a change of behaviour for the slightly odd case of specifying a day-of-year and then a month within the same time string:

echo DateTimeImmutable::createFromFormat("z m Y", '58 3 2019')->format('z: Y-m-d'), "\n";
# Previously "86: 2019-03-28"
# Now "117: 2019-04-28"

I'm not sure what to do about that, or even what the correct behaviour for that combination should be.
 [2020-05-10 18:14 UTC] rowan dot collins at gmail dot com
Branch updated with an alternative implementation that makes mixing 'z' with any month or day specifiers an error.
 [2020-05-10 18:37 UTC] rowan dot collins at gmail dot com
Now also raised as a PR against timelib: https://github.com/derickr/timelib/pull/80
 [2021-08-08 12:43 UTC] derick@php.net
-Status: Verified +Status: Closed -Assigned To: +Assigned To: derick
 [2021-08-08 12:43 UTC] derick@php.net
The fix for this bug has been committed.
If you are still experiencing this bug, try to check out latest source from https://github.com/php/php-src and re-test.
Thank you for the report, and for helping us make PHP better.

For 8.1.0beta3
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 13:01:29 2024 UTC