7

If I use this code I get strange results:

$datetime = new DateTime('2015-01-31');
$datetime->modify('+1 month');
echo $datetime->format('Y-m-t') . "<br>";
$datetime->modify('+1 month');
echo $datetime->format('Y-m-t') . "<br>";
$datetime->modify('+1 month');
echo $datetime->format('Y-m-t') . "<br>";

I get this:

2015-03-31
2015-04-30
2015-05-31

And not 2015-02-28.

How to fix?

  • @all: Is this a real bug ? This is clearly breaking usability and definitly breaking lots of applications. – Sliq Feb 05 '15 at 19:09

4 Answers4

4

The way DateTime works, + 1 month increases the month value by one, giving you 2015-02-31. Since there are only 28 or 29 days in February, that will evaluate to the first few days of March. Then, as you know, asking for Y-m-t will give you the last day of March.

Since you're already using t to get the last day of the month, you could avoid this problem by starting with a date that falls at the beginning of a month instead:

$datetime = new DateTime('2015-01-01');

Reference: PHP DateTime::modify adding and subtracting months

Community
  • 1
  • 1
mopo922
  • 6,293
  • 3
  • 28
  • 31
2

If you want to get the last day of the next month, you can use:

$datetime->modify('last day of next month');
Gergo Erdosi
  • 40,904
  • 21
  • 118
  • 94
1

You can try this function to add months to a datetime object

    /**
 * 
 * @param \DateTime $date DateTime object
 * @param int $monthToAdd Months to add at time
 */
function addMonth(\DateTime $date, $monthToAdd)
{
    $year = $date->format('Y');
    $month = $date->format('n');
    $day = $date->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)) {
        $newDate = \DateTime::createFromFormat('Y-n-j', $year . '-' . $month . '-1');
        $newDate->modify('last day of');
    } else {
        $newDate = \DateTime::createFromFormat('Y-n-d', $year . '-' . $month . '-' . $day);
    }
    $newDate->setTime($date->format('H'), $date->format('i'), $date->format('s'));

    return $newDate->format('Y-m-d');
}

echo addMonth(new \DateTime('2015-01-30'), 1); //2015-02-28
echo addMonth(new \DateTime('2015-01-30'), 2); //2015-03-30
echo addMonth(new \DateTime('2015-01-30'), 3); //2015-04-30
Alfredo Costa
  • 416
  • 3
  • 21
-2

Fix it like this.

$datetime = new DateTime('2015-01-31');
$datetime->modify('28 days');
echo $datetime->format('Y-m-t') . "<br>";
$datetime->modify('+1 month');
echo $datetime->format('Y-m-t') . "<br>";
$datetime->modify('+1 month');
echo $datetime->format('Y-m-t') . "<br>";

You will get

2015-02-28
2015-03-31
2015-04-30
abbas
  • 238
  • 2
  • 18
  • 3
    You are genius, really!! GENIUS!!! GENIUS!!! –  Feb 05 '15 at 18:36
  • Not really.. this doesn't work in general. Only if you already know that you're dealing with January, and then, well, why use `DateTime` to get you to February at all? Much better to pick a simple solution that always works and always looks the same, as mopo posted. – Lightness Races in Orbit Feb 05 '15 at 18:56
  • I'm joking when i'm saying you are genius. –  Feb 05 '15 at 20:15