I hope someone can help with this, i think perhaps the issue is i am overwriting the DateTime value in my second for loop, because its not outputting correct values, but not entirely sure.
<?php
$begin_from = new DateTime( "2023-01-01" );
$end_from = new DateTime( "2023-12-31" );
$begin_to = new DateTime( "2023-01-31" );
$end_to = new DateTime( "2023-12-31" );
for($i = $begin_from; $i <= $end_from; $i->modify(' 1 month')){
for($k = $begin_to; $k <= $end_to; $k->modify('first day of')->modify(' 1 month')->modify('last day of')){
echo $i->format("Y-m-d"),'..',$k->format("Y-m-d");
echo "\n";
}
}
From the code above its outputting this:
2023-01-01..2023-01-31
2023-01-01..2023-02-28
2023-01-01..2023-03-31
2023-01-01..2023-04-30
2023-01-01..2023-05-31
2023-01-01..2023-06-30
2023-01-01..2023-07-31
2023-01-01..2023-08-31
2023-01-01..2023-09-30
2023-01-01..2023-10-31
2023-01-01..2023-11-30
2023-01-01..2023-12-31
But, if you run these for loops separately you will get the correct values like below.
2023-01-01..2023-01-31
2023-02-01..2023-02-28
2023-03-01..2023-03-31
2023-04-01..2023-04-30
2023-05-01..2023-05-31
2023-06-01..2023-06-30
2023-07-01..2023-07-31
2023-08-01..2023-08-31
2023-09-01..2023-09-30
2023-10-01..2023-10-31
2023-11-01..2023-11-30
2023-12-01..2023-12-31
Can anyone tell what i am doing wrong here?
CodePudding user response:
I'm not sure why your code produces that output, but it can be simplified using a while
loop. You just need one date to manually modify within each loop, and one target date (DateTimeImmutable
is recommended for datetimes that are meant to remain unchanged)
$i_date = new DateTime( "2023-01-01" );
$end_date = new DateTimeImmutable( "2023-12-31" );
while($i_date <= $end_date){
// echo first of month..
echo $i_date->format("Y-m-d..");
// goto last of month
$i_date->modify('last day of');
// echo last of month \n
echo $i_date->format("Y-m-d") . "\n";
// goto first of next month for the next iteration
$i_date->modify('first day of')->modify(' 1 month');
}
The above will produce the following in PHP v7 :
2023-01-01..2023-01-31
2023-02-01..2023-02-28
2023-03-01..2023-03-31
2023-04-01..2023-04-30
2023-05-01..2023-05-31
2023-06-01..2023-06-30
2023-07-01..2023-07-31
2023-08-01..2023-08-31
2023-09-01..2023-09-30
2023-10-01..2023-10-31
2023-11-01..2023-11-30
2023-12-01..2023-12-31
PHP v5.6.40 requires you set the timezone or the default timezone like date_default_timezone_set('UTC');
before creating the DateTime
s, but then you get the same output as above.
CodePudding user response:
If your main goal is to output the start and end of each month within a given date range, inclusive of the end month, you can simplify this down to:
$from = new DateTime("2023-01-01");
$to = new DateTime("2023-12-31");
while ($from <= $to) {
echo $from->format("Y-m-01") . ".." . $from->format("Y-m-t") . "\n";
$from->add(new DateInterval("P1M"));
}
If you need to respect the $to
date, that is; if you need last date range to end at the $to
date, you can adjust the above code slightly using the min()
function:
$from = new DateTime("2023-01-01");
$to = new DateTime("2023-12-31");
while ($from <= $to) {
echo $from->format("Y-m-01") . ".." . min($to->format("Y-m-d"), $from->format("Y-m-t")) . "\n";
$from->add(new DateInterval("P1M"));
}
CodePudding user response:
To use datetime objects in a loop like @ArleighHix's answer, I'd only modify the start date and use a calls of min()
and max()
in conjunction with 01
and t
to ensure that date boundaries are not exceeded.
max()
and min()
are safe in this case because Y-m-d
is a "big-endian" date format -- this means that the string can be evaluated as a simple string.
Code: (Demo)
$start_date = new DateTime("2023-01-03");
$end_date = new DateTime("2023-12-15");
while ($start_date <= $end_date) {
printf(
"%s..%s\n",
max($start_date->format("Y-m-01"), $start_date->format("Y-m-d")),
min($end_date->format("Y-m-d"), $start_date->format("Y-m-t"))
);
$start_date->modify(' 1 month first day of');
}
If your actual project requirement is to have non-full date ranges each month, then a refactored approach would be necessary. Please provide a more challenging example by editing your question if this is a real concern/possibility.