PHP DateTime::modify adding and subtracting months
Why it's not a bug:
The current behavior is correct. The following happens internally:
+1 month
increases the month number (originally 1) by one. This makes the date2010-02-31
.The second month (February) only has 28 days in 2010, so PHP auto-corrects this by just continuing to count days from February 1st. You then end up at March 3rd.
How to get what you want:
To get what you want is by: manually checking the next month. Then add the number of days next month has.
I hope you can yourself code this. I am just giving what-to-do.
PHP 5.3 way:
To obtain the correct behavior, you can use one of the PHP 5.3's new functionality that introduces the relative time stanza first day of
. This stanza can be used in combination with next month
, fifth month
or +8 months
to go to the first day of the specified month. Instead of +1 month
from what you're doing, you can use this code to get the first day of next month like this:
<?php
$d = new DateTime( '2010-01-31' );
$d->modify( 'first day of next month' );
echo $d->format( 'F' ), "\n";
?>
This script will correctly output February
. The following things happen when PHP processes this first day of next month
stanza:
next month
increases the month number (originally 1) by one. This makes the date 2010-02-31.first day of
sets the day number to1
, resulting in the date 2010-02-01.
PHP subtract 1 month from date formatted with date ('m-Y')
<?php
echo $newdate = date("m-Y", strtotime("-1 months"));
output
07-2016
DateTime modify function skips February
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
Adding 2 months in PHP
You can use DateTime
class and modify method argument like last day of second month
$date = new DateTime('2014-12-31');
$date->modify('last day of second month');
echo $date->format('Y-m-d');
Edit::
modify
can have multiple possible arguments
last day of 2 month
last day of +2 month
Adding 12 months to February 29
Have a look!
function add_months($months, DateTime $dateObject)
{
$next = new DateTime($dateObject->format('Y-m-d'));
$next->modify('last day of +'.$months.' month');
if($dateObject->format('d') > $next->format('d')) {
return $dateObject->diff($next);
} else {
return new DateInterval('P'.$months.'M');
}
}
function getCalculatedDate($d1, $months)
{
$date = new DateTime($d1);
// call second function to add the months
$newDate = $date->add(add_months($months, $date));
//formats final date to m/d/Y form
$dateReturned = $newDate->format('m/d/Y');
return $dateReturned;
}
An example would be:-
$startDate = '02/29/2020';
$nMonths = 12; // choose how many months you want to add
$finalDate = getCalculatedDate($startDate, $nMonths); // output: 02/28/2021
This way you will get the output of 02/28/2021
PHP DateTime::modify adding and subtracting months
Why it's not a bug:
The current behavior is correct. The following happens internally:
+1 month
increases the month number (originally 1) by one. This makes the date2010-02-31
.The second month (February) only has 28 days in 2010, so PHP auto-corrects this by just continuing to count days from February 1st. You then end up at March 3rd.
How to get what you want:
To get what you want is by: manually checking the next month. Then add the number of days next month has.
I hope you can yourself code this. I am just giving what-to-do.
PHP 5.3 way:
To obtain the correct behavior, you can use one of the PHP 5.3's new functionality that introduces the relative time stanza first day of
. This stanza can be used in combination with next month
, fifth month
or +8 months
to go to the first day of the specified month. Instead of +1 month
from what you're doing, you can use this code to get the first day of next month like this:
<?php
$d = new DateTime( '2010-01-31' );
$d->modify( 'first day of next month' );
echo $d->format( 'F' ), "\n";
?>
This script will correctly output February
. The following things happen when PHP processes this first day of next month
stanza:
next month
increases the month number (originally 1) by one. This makes the date 2010-02-31.first day of
sets the day number to1
, resulting in the date 2010-02-01.
Subtracting days, months or years from date using php
use strtotime('date -years -months -days')
<?php
$time = strtotime('2001-11-14 -3 years -7 months -5 days');
echo $date = date("Y-m-d", $time);
How to add 1 month on a date without skipping i.e. february
It seems that there is no ready function for it, so I wrote it by myself.
This should solve my problem. Thanks anyway for your answers and comments.
If you will find some errors, please provide in the comments.
This function calculates the month and the year I will end in after adding some month. Then it checks if the date is right. If not, we have the case that the day is not in the target month, so we take the last day in the month instead.
Tested with PHP 5.3.10.
<?php
$monthToAdd = 1;
$d1 = DateTime::createFromFormat('Y-m-d H:i:s', '2011-01-30 15:57:57');
$year = $d1->format('Y');
$month = $d1->format('n');
$day = $d1->format('d');
$year += floor($monthToAdd/12);
$monthToAdd = $monthToAdd%12;
$month += $monthToAdd;
if($month > 12) {
$year ++;
$month = $month % 12;
if($month === 0)
$month = 12;
}
if(!checkdate($month, $day, $year)) {
$d2 = DateTime::createFromFormat('Y-n-j', $year.'-'.$month.'-1');
$d2->modify('last day of');
}else {
$d2 = DateTime::createFromFormat('Y-n-d', $year.'-'.$month.'-'.$day);
}
$d2->setTime($d1->format('H'), $d1->format('i'), $d1->format('s'));
echo $d2->format('Y-m-d H:i:s');
How to subtract 4 months from today's date?
Use the magic of strtotime
:
$fromDate = date("Ymd", strtotime("-4 months"));
Related Topics
Streaming a Large File Using PHP
How to Replace Microsoft-Encoded Quotes in PHP
Isset() and Empty() - What to Use
PHP String Replace Match Whole Word
How to Loop Through Two Arrays At Once
A Non Well Formed Numeric Value Encountered
Sorting a PHP Array of Arrays by Custom Order
Keeping Session Alive With Curl and PHP
Where Are $_Session Variables Stored
Getting Dom Elements by Classname
Storing Files in Database VS File System
Sum Values of Multidimensional Array by Key Without Loop
How to Generate All Permutations of a String in PHP