PHP DateTime::modify("+n month")
adds between 28 to 31 days to the current day, depending of the month and year.
Solution
I suggest you increment months from the first day of the month by using the modify
and format
methods:
// Instanciates the DateTime object.
$date = new DateTime("2020-01-01");
// Adds a month to the date.
$date->modify("+1 month"); // 2020-02-01
// Format the date with "t" (gets the last day of the month).
$date->format("Y-m-t"); // 2020-02-29
Incrementing from the first day of the month will never raise the February problem which is quite a quite common, thinking that PHP DateTime will smartly add a month from 2020-02-29
and output 2020-03-31
.
Why it happens?
In the Gregorian calendar, the average length of a month is 30.436875 days:
- 30 days in April, June, September and November;
- 31 days in January, March, May, July, August, October and December;
- 28 days or 29 days (in leap years) in February.
PHP will add to the current date the exact number of days there is in the given month.
Thus, PHP will adjust the date after the first increntation if you are incrementing from the
last day of the month.
E.g.:
Let's add a month from the final day of March (31th).
Since the current month (March) has 31 days in it, PHP will increment 31 days to the date. Adding 31 days from 2020-03-31
will result in skipping the whole month of April.
$date = new DateTime("2020-03-31"); // 2020-03-31
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-05-01 | Added 31 days (since March has 31 days).
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-06-01 | Added 31 days (since the new date is May 1st, which is a month with 31 days).
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-07-01 | Added 30 days
Now, let's add a month to the final day of April (30th).
We can see that since the next months all have 30+ days in it, final day will stay the same,
until February of the next year. Since February always has between 28 and 29 days, adding 31 days to it will pass the month, and resulting date will be March 2nd.
$date = new DateTime("2020-04-30");
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-05-30 | Added 30 days.
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-06-30 | Added 31 days.
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-07-30 | Added 30 days.
// ...
echo $date->modify("+1 month")->format("Y-m-d"); // 2021-01-30 | Added 31 days
echo $date->modify("+1 month")->format("Y-m-d"); // 2021-03-02 | Added 31 days (since January has 31 days).
echo $date->modify("+1 month")->format("Y-m-d"); // 2021-04-02 | Added 31 days (since the new date is March 2nd, which is a month with 31 days).
This is why it is recommended to increment months from the first day of the month,
since the 1st is common to all months.
$date = new DateTime("2020-01-01"); // 2020-01-01
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-02-01 | Added 31 days
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-03-01 | Added 29 days (since 2020 is leap year, yee haw).
echo $date->modify("+1 month")->format("Y-m-d"); // 2020-04-01 | Added 31 days