Handling timezone conversion with PHP DateTime

March 21, 2009
by David Mytton

The target audience for our server monitoring product, Server Density, is technical users – sysadmins and developers. As such, each weekend we are going to publish a technical article focused on a technology we use or a programming problem we have solved.

Last night, we pushed out a rewrite of the timezone handling in SD. This now allows you to select your location (or nearest major city within your timezone) so the dates/times displayed in the interface can be automatically converted to your timezone, including handling of DST. This is accomplished using the new DateTime class introduced into PHP 5.2. Prior to the release of 5.2, conversion into different timezones required either use of the Pear Date package or manually calculating offsets.

The first complexity that you need to deal with as a programmer is how the timezones are calculated relative to the server’s default timezone setting. Since PHP 5.1, all the date/time functions create times in the timezone of the server. The timezone can be set programmatically as of PHP 5.2 using the date_default_timezone_set() function. So if you call the date() function without specifying a timestamp as the second parameter and the timezone is set to GMT, then the date will be in the +0000 timezone; equally if you set the timezone to be New York and it is winter, the timezone will be -0500 (-0400 in summer).

This means that if you need the date in GMT then you need to know what the offset is of the date you’re working with so that if necessary, you can do the calculation to convert it to +0000. When would you need to do this? Well, the MySQL TIMESTAMP field type stores the timestamp internally using GMT (UTC) but always returns it in the server’s timezone. This means if you want to do any SELECT statements, you always need to convert the timestamp you pass in your SQL to UTC.

This sounds quite complex but in reality, you can use the DateTime class to do most of the hard work. You first need to get the user to specify their timezone. This will then be attached to any DateTime object you create so that the right offset can be calculated based on it. The PHP manual provides a list of all the acceptable timezone strings which can be used. There is also a PHP function which will output the list of timezones. Server Density uses this to generate a list of timezones as a drop menu for the user to select from.

Once you have the user’s timezone stored, you can create a DateTimeZone object from it. This will be used to do all the offset calculations.

$userTimezone = new DateTimeZone($userSubmittedTimezoneString);

Now to convert a date/time into the user’s timezone, you simply need to create it as a DateTime object:

$myDateTime = new DateTime('2009-03-21 13:14');

This will create a DateTime object which has the time specified. The parameter accepts any format supported by strtotime() and if you leave it empty, it will default to “now”.

It is important to note that the time created will be in the timezone that has been set as the default for the server. This is relevant because the offset calculated will be relative to that timezone. For example, if the server is GMT and you want to convert to Paris time, it will require adding 1 hour. However, if the server is in the Paris timezone then the offset will be zero. You can force the timezone that you want $myDateTime to be in by specifying the second parameter as a DateTimeZone object. So for example, if you want it to be 13:14 on 21st March 2009 in GMT, then you would need to use this code:

$gmtTimezone = new DateTimeZone('GMT');
$myDateTime = new DateTime('2009-03-21 13:14', $gmtTimezone);

To double check, you can do:

echo $myDateTime->format('r');

which would output Sat, 21 Mar 2009 13:14:00 +0000.

The final step is to work out the offset from your DateTime object to the user’s timezone so you can do the appropriate calculation to convert it into that timezone. This is where the $userTimezone DateTimeZone object comes in because you use the getOffset() method:

$offset = $userTimezone->getOffset($myDateTime);

This will return the number of seconds that you need to add to $myDateTime to convert it into the user’s timezone. Therefore:

$userTimezone = new DateTimeZone('America/New_York');
$gmtTimezone = new DateTimeZone('GMT');
$myDateTime = new DateTime('2009-03-21 13:14', $gmtTimezone);
$offset = $userTimezone->getOffset($myDateTime);
echo $offset;

will print -14400, or 4 hours (because New York is on DST).

This is where the DateTime::add method would come in handy because we could just add the offset. Unfortunately, it is not available until PHP 5.3 so we need to do the calculation manually by converting to Unix timestamp.

echo date('Y-m-d H:i', $myDateTime->format('U') + $offset)

would output 2009-03-21 09:14 which is the correct conversion from 2009-03-21 13:14 London GMT to New York time.

8 Responses leave one →
  1. July 31, 2009

    Thanks for this, helped me find the answer I was looking for.

    Instead of calculating the offset I use setTimezone by replacing:
    $offset = $userTimezone->getOffset($myDateTime);
    echo date(‘Y-m-d H:i’, $myDateTime->format(‘U’) + $offset);

    with:
    $myDateTime->setTimezone($userTimezone);
    echo $myDateTime->format('Y-m-d H:i');

    This is working in PHP 5.2.8

  2. August 3, 2009

    Have to implement timezone functionality on my site. The article provides a good starting point. Thanks.

  3. October 13, 2009
    Blake permalink

    We use the same method as Chris suggested (i.e. use setTimezone to switch between timezones). Using the setTimezone method seems cleaner than calculating the offset and adding it to the date.

  4. March 5, 2010
    Josh permalink

    You can shorten the code even more by doing this (converts GMT to New York):

    $myDateTime = new DateTime('2009-03-21 13:14', new DateTimeZone('GMT'));
    $myDateTime->setTimezone(new DateTimeZone('America/New_York'));
    echo $myDateTime->format('Y-m-d H:i');

    • March 5, 2010

      This doesn’t do any conversion, it just changes the timezone assigned to the DateTime object. It’s what we tried first and it doesn’t work.

      • March 5, 2010
        Josh permalink

        I’m not sure what you are talking about, but it appears to work fine with PHP 5.2.11:

        $myDateTime = new DateTime(‘2009-03-21 13:14′, new DateTimeZone(‘GMT’));
        echo $myDateTime->format(‘r’);

        outputs Sat, 21 Mar 2009 13:14:00 +0000

        $myDateTime->setTimezone(new DateTimeZone(‘America/New_York’));
        echo $myDateTime->format(‘r’);

        outputs Sat, 21 Mar 2009 09:14:00 -0400

      • March 11, 2010
        Sam permalink

        Oddly enough, I couldn’t get the original code to work while converting from an Eastern start time to anything else. The one Josh posted works perfectly. Thanks to both for providing a straightforward solution.

Trackbacks & Pingbacks

  1. Automatic timezone conversion in JavaScript « Boxed Ice Blog

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS