php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #68981 negative relative month values in strtime() give nonsense results
Submitted: 2015-02-03 18:36 UTC Modified: 2021-04-05 16:01 UTC
Votes:3
Avg. Score:4.7 ± 0.5
Reproduced:2 of 2 (100.0%)
Same Version:1 (50.0%)
Same OS:2 (100.0%)
From: teo8976 at gmail dot com Assigned:
Status: Not a bug Package: Date/time related
PHP Version: 5.6.5 OS: linux
Private report: No CVE-ID: None
 [2015-02-03 18:36 UTC] teo8976 at gmail dot com
Description:
------------
The way negative relative month values are handled by strtotime() is
1) undocumented
2) complete nonsense

example: strtotime("-3 months", $date)

Test script:
---------------
$end_date=mktime(23,59,59,12,31,2014);
echo date("r", $end_date)."\n";
$date=strtotime("-1 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-2 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-3 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-4 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-5 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-6 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-7 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-8 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-9 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-10 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-11 months", $end_date);
echo date("r", $date)."\n";
$date=strtotime("-12 months", $end_date);
echo date("r", $date)."\n";

Expected result:
----------------
Wed, 31 Dec 2014 23:59:59 +0100
Mon, 30 Nov 2014 23:59:59 +0100
Fri, 31 Oct 2014 23:59:59 +0100
Wed, 30 Sep 2014 23:59:59 +0200
Sun, 31 Aug 2014 23:59:59 +0200
Thu, 31 Jul 2014 23:59:59 +0200
Tue, 30 Jun 2014 23:59:59 +0200
Sat, 31 May 2014 23:59:59 +0200
Thu, 30 Apr 2014 23:59:59 +0200
Mon, 31 Mar 2014 23:59:59 +0200
Mon, 28 Feb 2014 23:59:59 +0100
Fri, 31 Jan 2014 23:59:59 +0100
Tue, 31 Dec 2013 23:59:59 +0100

Actual result:
--------------
Wed, 31 Dec 2014 23:59:59 +0100
Mon, 01 Dec 2014 23:59:59 +0100
Fri, 31 Oct 2014 23:59:59 +0100
Wed, 01 Oct 2014 23:59:59 +0200
Sun, 31 Aug 2014 23:59:59 +0200
Thu, 31 Jul 2014 23:59:59 +0200
Tue, 01 Jul 2014 23:59:59 +0200
Sat, 31 May 2014 23:59:59 +0200
Thu, 01 May 2014 23:59:59 +0200
Mon, 31 Mar 2014 23:59:59 +0200
Mon, 03 Mar 2014 23:59:59 +0100 << THIS IS PARTICULARLY HILARIOUS
Fri, 31 Jan 2014 23:59:59 +0100
Tue, 31 Dec 2013 23:59:59 +0100

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-02-03 19:04 UTC] requinix@php.net
-Status: Open +Status: Not a bug
 [2015-02-03 19:04 UTC] requinix@php.net
You told strtotime() to subtract months from a date so it did that. But rather than error out when it arrived at dates like November 31st or February 31st, it wrapped the date to the next month. Like mktime() would have.

If you want the last day of each month then you have to say that.
http://3v4l.org/QSorH
 [2015-02-03 20:18 UTC] teo8976 at gmail dot com
Well, then the documentation doesn't accurately describe the behavior.

From http://php.net/manual/en/datetime.formats.relative.php
"Note:
Relative month values are calculated BASE ON THE LENGTH OF MONTHS 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."

The only sensible way to extend that reasoning (the one in documentation, which is completely different from yours) to subtractions is to assume that the number of days subtracted is the number of days of the month to which the starting date belong, plus the number of days of the previous month, etc.

According to that statement in documentation, relative formats with dates are not supposed to add/subtract to the index of the month and then "wrap". They are supposed to add/subtract the number of days that correspond to each month.

Instead, it does seem to behave the way you describe, even for positive months.

So either the documentation or the behavior needs to be fixed.
It would definitely make more sense to fix the behavior, because the one described in the documentation is much more desirable and intuitive.
 [2015-02-03 20:27 UTC] requinix@php.net
-Status: Not a bug +Status: Open
 [2015-02-03 20:37 UTC] teo8976 at gmail dot com
Indeed I think you are right that the current behavior makes sense,
but documentation then is definitely wrong and needs to be corrected.
See https://bugs.php.net/bug.php?id=60353#1422995592
 [2018-02-27 01:20 UTC] calebpitman at gmail dot com
The documentation may address this, but ultimately this makes no sense:
+1 month from 01/31/2018 = 03/03/2018
+1 month from 02/01/2018 = 03/01/2018

The difference of a month should change the actual value of the month. The unit of measurement isn't days, regardless of how many days are in the month. The unit is months. Because a month has a different amount of days shouldn't affect the outcome. December -1 month is logically November. If it's the last day of the month, it would logically fall on the last day of the month in November. 

+1 month from 1/31/2018 would logically be 2/28/2018 or 2/29/2018, because it's a unit of months. 

The very last second of 1/31/2018 is 1517443199.

+1 month from 1517443199 should be the very last second of February, because the unit is months.
 [2021-04-05 16:01 UTC] derick@php.net
-Status: Open +Status: Not a bug
 [2021-04-05 16:01 UTC] derick@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 25 14:01:31 2024 UTC