The confusing strtotime() function in PHP

Summary

PHP's strtotime() function can lead to unexpected date calculations when dealing with relative month modifiers like "-1 month" due to its date canonicalization logic. When a calculated date, such as June 31st from July 31st minus one month, is invalid, strtotime() adjusts it to the next valid day, often resulting in the first day of the subsequent month. This behavior is consistent with how PHP handles non-existent dates. To achieve precise results, PHP 5.3+ introduced "first day of" and "last day of" tokens, enabling developers to correctly target specific days within relative months. For older PHP versions, mktime() offers a viable alternative for similar date manipulations.

Frequently PHP programmers get confused of the use of i month, -1 month, next month in strtotime() function. and hence it leaves some impression to programmer that this function is not that reliable.

Let's take one example of strtotime call with -1 month and see why it leaves this impression.

date("Y-m-d",strtotime("-1 month"))  // Assume today is 2018-07-31

What's the output of above call? The answer is 2018-07-01. Why not 2018-06-30? So people get confused. It appears that this is wrong at first glance, but if think again, this output is reasonable.

Below is how the logic works

  • It first minus one month, so the date becomes 2017-06-31
  • Since June does not have 31 days, hence it will be canonicalized and becomes 2018-01-01. This is similar to why 2:60 will be 3:00

Now you get the point. This logic can also be verified with below call.

var_dump(date("Y-m-d", strtotime("2018-06-31")));

The output will be 2018-07-01. This behavior also applies to other months which have different days. Below are some more examples:

var_dump(date("Y-m-d", strtotime("-1 month", strtotime("2018-03-31"))));
// Output 2018-03-03
var_dump(date("Y-m-d", strtotime("+1 month", strtotime("2018-08-31"))));
// Output 2018-10-01
var_dump(date("Y-m-d", strtotime("next month", strtotime("2018-01-31"))));
// Output 2018-03-03
var_dump(date("Y-m-d", strtotime("last month", strtotime("2018-03-31"))));
// Output 2018-03-03

So how to make it work as expected? Starting from PHP 5.3, strtotime() added a few new tokens which can be used to correct this. They are first day of and last day of

var_dump(date("Y-m-d", strtotime("last day of -1 month", strtotime("2018-03-31"))));
// Output 2018-02-28
var_dump(date("Y-m-d", strtotime("first day of +1 month", strtotime("2018-08-31"))));
// Output 2018-09-01
var_dump(date("Y-m-d", strtotime("first day of next month", strtotime("2018-01-31"))));
// Output 2018-02-01
var_dump(date("Y-m-d", strtotime("last day of last month", strtotime("2018-03-31"))));
// Output 2018-02-28

What if people are still using old version of PHP? mktime() could be used to achieve similar result.

Hence don't get panic when see this kind of "unexpected" behavior in the future.

Reference: http://www.laruence.com/2018/07/31/3207.html

PHP STRTOTIME FIRST DAY OF -1 MONTH

  RELATED

  COMMENTS

0

No comment for this article.