7

I have a script that gets the current and last month in PHP like so:

$currentMonth = date('m');
//Expected:07
//Result:07
$lastMonth = date('m', strtotime('-1 months'));
//Expected:06
//Result:07

Today happens to be the 31 or end of the month of July. Is this result to be expected from PHP?

When using -31 days the result is as expected:

$lastMonth = date('m', strtotime('-31 days'));
//Expected:06
//Result:06
Subie
  • 373
  • 2
  • 16
  • month-math is always risky. what IS a month? "today in ($curmonth-1)"?, what happens if it's march 31st, do you want february 31st, or feb 28? feb 29? – Marc B Jul 31 '15 at 16:15
  • 1
    Looks like those aren't the only days `-1 month` breaks on: http://codepad.viper-7.com/E4gP0W The 31st of a month don't surprise me, but the 29th and 30th of March do. – Mr. Llama Jul 31 '15 at 16:38
  • 1
    Always amazed when I find someone still not using https://github.com/briannesbitt/Carbon to do dates in PHP. – ceejayoz Jul 31 '15 at 16:46
  • @ceejayoz Carbon reacts the same in this case https://github.com/briannesbitt/Carbon/issues/37 – Subie Jul 31 '15 at 17:20
  • @Subie I'm still surprised though. So much easier. – ceejayoz Jul 31 '15 at 17:42
  • 1
    Not just php, but also gnu http://www.gnu.org/software/tar/manual/html_node/Relative-items-in-date-strings.html#SEC120 – Josh J Jul 31 '15 at 17:47

5 Answers5

2

You can do this way

$d = new DateTime(); 
$currentMonth = $d->format('m');
//Expected:07
//Result:07
print $currentMonth;

$d->modify('first day of previous month');
print "<br/>";
$lastMonth = $d->format('m'); 
//Expected:06
//Result:06
print $lastMonth;

DEMO: http://codepad.viper-7.com/kokWi8

A l w a y s S u n n y
  • 36,497
  • 8
  • 60
  • 103
2

This is a issue with PHP's date-string parser. See here: http://derickrethans.nl/obtaining-the-next-month-in-php.html

@Mr. Llama made a script showing what other dates this issue effects:http://codepad.viper-7.com/E4gP0W

The solution I went with:

//Date:07/31/15
$currentMonth = date('m');
//Result:07
$lastMonth = date('m', strtotime('first day of -1 months'));
//Result:06
Subie
  • 373
  • 2
  • 16
2

Here's a cleaner test case that doesn't expire:

<?php
$origin = mktime(18, 0, 0, 7, 31, 2015);
var_dump( date('r', $origin), date('r', strtotime('-1 months', $origin)) );
string(31) "Fri, 31 Jul 2015 18:00:00 +0200" 
string(31) "Wed, 01 Jul 2015 18:00:00 +0200"

I'm pretty sure it's a documentation issue, because the manual clearly states this (emphasis mine):

Relative month values are calculated based on the length of months that they pass through. An example would be "+2 month 2011-11-30", which would produce "2012-01-30". This is due to November being 30 days in length, and December being 31 days in length, producing a total of 61 days.

... and it's wrong.

PHP bug tracker has tons of dupes about this. They're all closed as not a bug. Here's a relevant comment from 2009 that explains it:

I agree that this is an annoying behaviour.

Also, the implementation is problematic. Basically if you use '+1 month' it takes the month number, adds 1 and parses result as a new date.

If you use '+1 month' on the first of the month, it sets the date to the next first of the month.

This behaviour gives the impression, that php considers the length of a month, which is not true.

But if you use '+1 month' on the last day of a month, the result is unexpected as 2009-05-31 becomes 2009-06-31 which is an invalid date and then interpreted as 2009-07-01.

This should at least be mentioned in the documentation.

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
1

-1 month is interpreted as "same day of month, last month". If this day does not exist, the date overflows into the next month. Actually the result is the same as strtotime("31.6.2015") - try it!

Fabian Schmengler
  • 24,155
  • 9
  • 79
  • 111
-1

There is "s" in excess in month. It should be like this:

$lastMonth = date('m', strtotime('-1 month'));
A l w a y s S u n n y
  • 36,497
  • 8
  • 60
  • 103
tdebroc
  • 1,436
  • 13
  • 28
  • 5
    makes no difference. PHP isn't a grammar nazi and `1 months` and `1 month` are identical as far as it's concerned. – Marc B Jul 31 '15 at 16:16
  • 1
    Indeed, the extra `s` is allowed/optional. The [documentation](http://php.net/manual/en/datetime.formats.relative.php) confirms that. – axiac Jul 31 '15 at 16:27