php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #79104 Relative DateInterval Creates Infinite Loop
Submitted: 2020-01-11 21:42 UTC Modified: 2022-06-05 14:31 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: jimbo2150 at gmail dot com Assigned: derick (profile)
Status: Wont fix Package: Date/time related
PHP Version: 7.3.13 OS:
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: jimbo2150 at gmail dot com
New email:
PHP Version: OS:

 

 [2020-01-11 21:42 UTC] jimbo2150 at gmail dot com
Description:
------------
A relative date format used in DateInterval produces and infinite loop when used with DatePeriod.

An example is looking for the first Monday in January of each year. When iterated via DatePeriod the year will not get iterated and the correct relative date for current year keeps getting returned creating an infinite loop.

Test script:
---------------
// Looking for every Monday of January each year
$fmj = DateInterval::createFromDateString('first Monday of January');

$period = new DatePeriod(new DateTime('2020-01-01'), $fmj, new DateTime('2022-01-11'));

// Infinite loop, first Monday of January of current year keeps getting iterated
foreach ($period as $dt) {
    var_dump($dt->format("l Y-m-d H:i:s"));
}

Expected result:
----------------
Return each first Monday of January for the next 2 years.

string(30) "Wednesday 2020-01-01 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2021-01-04 00:00:00"
string(27) "Monday 2022-01-03 00:00:00"

Actual result:
--------------
Infinite loop where each date is the first Monday of the current year's January.

string(30) "Wednesday 2020-01-01 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
string(27) "Monday 2020-01-06 00:00:00"
...
{infinite loop}

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-01-19 11:04 UTC] a at b dot c dot de
Looks like it can't determine how long "first Monday of January" is.

echo $fmj->format('%y years, %m months, %d days, %h hours, %i minutes, %s seconds');

What numbers should it be using for those fields?
 [2020-01-20 16:53 UTC] girgias@php.net
-Assigned To: +Assigned To: derick
 [2020-01-23 21:41 UTC] jimbo2150 at gmail dot com
Not sure what you mean by the numbers you are looking for.

I got closer with this code (added "next year" to the DatePeriod object, but the result is erratic (not sure what is being calculated):


$fmj = DateInterval::createFromDateString('first Monday of January next year');

$period = new DatePeriod(new DateTime('first Monday of January 2020'), $fmj, new DateTime('2022-01-31'));

foreach ($period as $dt) {
    var_dump($dt->format("l Y-m-d H:i:s"));
}

// Strange, incorrect output (not the first Monday of January 2021 or 2022):
string(26) "Monday 2020-01-06 00:00:00"
string(29) "Wednesday 2021-01-06 00:00:00"
string(27) "Tuesday 2022-01-04 00:00:00"
 [2020-01-23 23:52 UTC] antonino dot spampinato86 at gmail dot com
$timezone = new DateTimeZone( "UTC" );
$fmj = DateInterval::createFromDateString( 'first Monday of January 2020' );
//Monday from start 2019 only
$period = new DatePeriod( new DateTime( '2019-01-01 00:00:00.000000', $timezone ), $fmj, new DateTime( '2022-01-11 00:00:00.000000', $timezone ) );

$y = 0;
foreach ( $period as $dt ) {

    $display[$y++] = $dt->format( "l Y-m-d H:i:s" );
		//stop total_recurrence
		if( $y == 5 ) {
			break;
		}
}
Manually stop infinite loop :)
The relative date, first Monday of jenaury is extended for the current year but for the parametres start and end of function DatePeriod , only uses the start, there are two bugs in the php source code, the reference is not counted if the code does not return the expected result, the second bug is stopping on start.
 [2020-01-24 12:15 UTC] antonino dot spampinato86 at gmail dot com
observe the behavior of DateTime, this code expands the relative date for the current year while for DatePeriod it must not expand for the current year but must use the value for the start and end year, it means if I have a year explicitly expressed DatePeriod result must be for the year expressed otherwise if the year is not expressed it recovers the start and end of DatePeriod.
$var = new DateTime( 'first monday of January', new DateTimeZone( "UTC" );
$display[5] = $var->format( "l Y-m-d H:i:s" );
var_dump( $display );
 [2021-02-03 17:24 UTC] cmb@php.net
> but the result is erratic (not sure what is being calculated)

The algorithm selects the first Monday of January of the *current*
year, and then advances 1 year.  Not sure, how to categorize that.
 [2021-04-06 19:23 UTC] derick@php.net
-Type: Bug +Type: Feature/Change Request
 [2021-04-06 19:23 UTC] derick@php.net
I'm not sure whether it is possible to do this with a one-shot advancement yet.
 [2021-07-28 13:00 UTC] antonino dot spampinato86 at gmail dot com
DateInterval can indicate the progress of the year, while in the second form it can be applied to the current year. In the second you can use modify or directly in the constructor, since you are not using a range between two dates.

The progress of the year DateInterval::createFromDateString ('first Monday of January next year');.

Specific year not supported by Dateinterval new DateTime ('first monday of January', new DateTimeZone ("UTC")); without specifying is the same as saying the current year.

Currently DataPeriod incorrectly calculates the distance between two relative dates, expressed previously. Fifth I ask that Dateinterval throw an exception if with that relative date.

https://3v4l.org/uJpiU
 [2021-07-28 13:31 UTC] antonino dot spampinato86 at gmail dot com
This work first Monday of next month between two date, https://3v4l.org/R8dL6

For the first link I added I'm thinking it lacks support for month properties ("first Monday January next year"), so DatePeriod incorrectly calculates between two dates. 'next year'.
 [2022-06-05 14:31 UTC] derick@php.net
-Status: Assigned +Status: Wont fix
 [2022-06-05 14:31 UTC] derick@php.net
I am marking this as "won't fix" as this is not doable with the current architecture. You will have to make your own loop, and use the ->modify() method more than once in each iteration to obtain this result:

$current = new DateTimeImmutable('2020-01-01');

while ( $current < new DateTimeImmutable('2022-01-11') )
{
    $current = $current->modify('monday');
    echo $current->format('Y-m-d'), "\n";
    $current = $current->modify('next year January 1st');
}
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 12:01:31 2024 UTC