It's not a bug, and I'm surprised no answer makes any mention of this. It's what happens when we run it on a day like the 31st. It gets the next month and if that doesn't have 31 days, it just skips it.
Just see what I got when I ran your code today (31 Aug 2023):
October 2023 //Skips September because it doesn't have day 31
2023-10-01
2023-10-31
October 2023 //Ditto for November
2023-10-01
2023-10-31
December 2023
2023-12-01
2023-12-31
If you want to skip using DateInterval
and still use strtotime()
, just replace "+1 month" with "last day of +1 month".
Like so: strtotime('+'.$i.' month')
-> strtotime('last day of +' . $i . ' month')
for($i = 1; $i < 4; $i++) { // For each month for 3 months
$monthTitle = date('F Y', strtotime('last day of +' . $i . ' month'));
$begin_date = date('Y-m-01', strtotime('last day of +' . $i . ' month')); // First day of calendar month in future.
$end_date = date('Y-m-t', strtotime('last day of +' . $i . ' month')); // Last day of calendar months in future.
}
Output:
September 2023 //Works as we wanted
2023-09-01
2023-09-30
October 2023
2023-10-01
2023-10-31
November 2023 //Works as we wanted
2023-11-01
2023-11-30
There are these closed bug reports one in 2003 and another in 2008 from php.net. See also SeanDowney's explanation for "How to get previous month and year relative to today, using strtotime and date?"
However, I found that most of us agree that adding some mention of this behaviour in the function would save us the trouble. Worse cases would be when it tries to get the dreaded February on days 29-31 of any month.
A month is an ill-defined unit in the end and I'll leave you with this infamous comment by rasmus from those bug reports,
"If I told you on January 30 that I would come back in exactly one month to beat the crap out of you, when would you think I would show up?"