PHP Datetime::Modify Adding and Subtracting Months

PHP DateTime::modify adding and subtracting months

Why it's not a bug:

The current behavior is correct. The following happens internally:

  1. +1 month increases the month number (originally 1) by one. This makes the date 2010-02-31.

  2. 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:

  1. next month increases the month number (originally 1) by one. This makes the date 2010-02-31.

  2. first day of sets the day number to 1, 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. +1 month increases the month number (originally 1) by one. This makes the date 2010-02-31.

  2. 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:

  1. next month increases the month number (originally 1) by one. This makes the date 2010-02-31.

  2. first day of sets the day number to 1, 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



Leave a reply



Submit