Avoiding Timestamp Errors When Calculating Dates in PHP

A note from A Beautiful Site’s founder: We developed Surreal to be easy for you and your clients. If you’re a web designer, you should take a look at the simple, hosted CMS that’s changing the way content is managed. Surreal integrates in moments and is trusted by over 18,000 websites. Try it out for free and let us know what you think! Visit Website »

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
If you enjoyed this article, please share it with a friend!

9 Responses to Avoiding Timestamp Errors When Calculating Dates in PHP

  1. Therese says:

    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.

  2. Cory LaViska says:

    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.

  3. Speedovation says:

    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

  4. Speedovation says:

    ans is \\a\\t

    rd/th/st works according to date

  5. gns100 says:

    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.

  6. Luchiano says:

    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 ?

  7. Cory LaViska says:

    @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.

  8. Rudy says:

    Question… When programming, what is the proper timestamp representation for a particular day? Noon of the day in question?

  9. Travis G says:

    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()));

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>