When calculating dates in PHP, it is easy to add millisecond values such as 60*60*24 to increment the time by one day. This can become problematic for two main reasons.
Issues with Leap Years
A year has approximately 365.25 days, hence a leap year occurs once every four years. If you start on Tuesday April 1, 2003 at 12:00:00 the timestamp would be 1049198400. Adding the length of one day (60*60*24, or 86,400ms) would give us 1049198400 + 86,400 = 1049284800, which is the same as Wed April 02, 2003 12:00:00. No problem here.
Using this same methodology, we realize that adding one year (60*60*24*365, or 31,536,000ms) to Wednesday April 02, 2003 12:00:00 would result in 1049284800 + 31,536,000 = 1080820800. This is problematic because 1080820800 is the timestamp for Thursday 01 April 2004 12:00:00 (we were expecting the next day, 02 April).
Even if we used 60*60*24*365.25 (or 31,557,600ms) to compensate for the quarter-day phenomenon, we would end up with 1049284800 + 31,557,600 = 1080842400, which is Thursday 01 Apr 2004 18:00:00. This “miscalculation” is due to the leap day that occurred on 29 February 2004.
Issues with Daylight Saving Time
Another issue involves Daylight Saving Time (DST). The calculation is affected similarly and a one hour shift forward or backward will occur if your version of PHP is configured to account for DST. This can turn out to be more drastic than a one hour shift, however. Consider this:
$current_date = "2007-11-04";
$timestamp = strtotime($current_date);
$timestamp += 60*60*24; // one day increment
$new_date = date("Y-m-d", $timestamp);
On 04 November 2007, the time “falls back” one hour, thus $new_date contains the same value as $current_date when $current_date is equal to 2007-11-04. Here are some sample outputs surrounding 04 November 2007:
| $current_date | → | $new_date |
|---|---|---|
| 2007-11-01 | → | 2007-11-02 |
| 2007-11-02 | → | 2007-11-03 |
| 2007-11-03 | → | 2007-11-04 |
| 2007-11-04 | → | 2007-11-04 |
| 2007-11-05 | → | 2007-11-06 |
| 2007-11-06 | → | 2007-11-07 |
Consequently, if we were outputting hours, minutes and seconds as well, every day after the DST change would be exactly one hour off (until, of course, the next DST change canceled it out).
A Better Way to Calculate Dates
Fortunately, we can overcome both of these hurdles simply by using the strtotime() function. Instead of adding one day to a timestamp like this:
$date += 60*60*24;
We can successfully add one day like this:
$date = strtotime(date("Y-m-d", strtotime($date)) . " +1 day");
Similarly, we can use the same method for weeks, months, years, etc.:
$date = strtotime(date("Y-m-d", strtotime($date)) . " +1 week");
$date = strtotime(date("Y-m-d", strtotime($date)) . " +2 week");
$date = strtotime(date("Y-m-d", strtotime($date)) . " +1 month");
$date = strtotime(date("Y-m-d", strtotime($date)) . " +30 days");
Using the strtotime() function to add relative blocks of time results in accurate calculations where the previously shown method sometimes fails.
What if you wanted to add one month to $date? Using the previous method, you would be forced to estimate the duration of a month or create a really smart algorithm to figure out the exact duration of the given month. Since each month varies in length, the first option is unreliable. The second option has already been incorporated into the strtotime() function, so why not use that instead?
More Examples
You can use the following examples to experiment with. For further documentation, see the PHP Manual‘s section on strtotime().
$date = "2005-05-10";
echo date("Y-m-d", strtotime(date("Y-m-d", strtotime($date)) . " +30 days")); // output is 2005-06-09
echo date("Y-m-d", strtotime(date("Y-m-d", strtotime($date)) . " +4 weeks")); // output is 2005-06-07
echo date("Y-m-d", strtotime(date("Y-m-d", strtotime($date)) . " +1 month")); // output is 2005-06-10
Thanks for the tip- there’s an error though in the code. If $date is a timestamp (in order to perform
the original calculation
$date += 60*60*24;
)
then the code using strtotime should be:
$date = strtotime(date(“Y-m-d”, $date) . ” +1 day”);
*without* the embedded call strtotime($date),
since date() takes a second timestamp parameter.
Therese, you are correct. However, I wouldn’t actually call it an error in the code since the extra strtotime() is redundant and doesn’t cause erroneous output.
date(“l, F jS, Y n:i A”,strtotime($posts['PostedDate']));
This gives
Monday, February 3rd, 2003 2:11 PM
Now I want
Monday, February 3th, 2003 at 2:11 PM
I mean want to add “at” in b/w and rd to be replaced with th
..Info is nice here
ans is \\a\\t
rd/th/st works according to date
Great tip. I had a program that was incrementing by days, and got stuck on 10-29-2006. It took me a little time to realize it was a daylight savings thing.
I had a problem with this. I tried to add one month to 2009-03-31 09:44:45 and I get 2009-05-01 09:44:45. Any idea ?
@Luchiano: please refer to this bug report for the reason this happens: http://bugs.php.net/bug.php?id=22486&edit=2
So, it’s not actually a bug, it’s just that PHP uses 30.5 as an average constant for “1 month”. If you’re using PHP 5.3 or higher, you can use “last day of next month” in strtotime() to get the results you’re expecting.
Question… When programming, what is the proper timestamp representation for a particular day? Noon of the day in question?
Awesome post! I was wondering a clean way to do this… you saved me from subtracting the time of day and day of month, then the number of days of the last month, to get a previous month! :)
@Rudy I believe the best way is to omit the time of day, i.e. strtotime(date(“Y/n/j”, $time()));