Handling timezone conversion with PHP DateTime

Handling timezone conversion with PHP datetime 1

By David Mytton,
CEO & Founder of Server Density.

Published on the 22nd September, 2016.


Editor’s note: This is an updated version of an article originally published on 21/03/2009.

Back in 2009 we introduced a new location preference feature for Server Density. Users could now specify their desired location, and then all dates/times automatically converted to their timezone (including handling of DST). We did that by using the DateTime class that was introduced with PHP 5.2.

Your very first challenge related to timezones is to deal with how they are calculated relative to the server’s default timezone setting. Since PHP 5.1, all the date/time functions create times in the server timezone of the server. And as of PHP 5.2 you can set the timezone programmatically 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 default to the +0000 timezone. Equally, if you set the timezone to New York in winter time the timezone will be -0500 (-0400 in summer).

The ins and outs of handling timezone conversion

If you want the date in GMT, you need to know the offset of the date you’re working with so you can convert it to +0000, if necessary. 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. So, for any SELECT statements you will need to convert the timestamp you pass in your SQL to UTC.

This might sound complicated but you can let the DateTime class do most of the hard work. You first need to get the user to specify their timezone. This will be attached to any DateTime object you create so the right offset can be calculated. The PHP manual provides a list of all the acceptable timezone strings.

There is also a PHP function that outputs the list of timezones. Server Density uses this to generate a list of timezones as a drop-down menu for the user to select from.

DateTimeZone Object

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

$userTimezone = new DateTimeZone($userSubmittedTimezoneString);

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

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

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

Note that the time created will be in the default timezone of the server. This is relevant because the calculated offset will be relative to that timezone. For example, if the server is on 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. If,  for example, you wanted it to be 13:14 on 21st March 2016 in GMT, you’d need to use this code or something similar:

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

To double check, you can run:

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

which would output Mon, 21 Mar 2016 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 convert it to that timezone. This is where the $userTimezone DateTimeZone object comes in (because we use the getOffset() method):

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

This will return the number of seconds 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('2016-03-21 13:14', $gmtTimezone);
$offset = $userTimezone->getOffset($myDateTime);
echo $offset;

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

DateTime::add

As of PHP 5.3, you can also use DateTime::add method to create the new date just by adding the offset. So:

$userTimezone = new DateTimeZone('America/New_York');
$gmtTimezone = new DateTimeZone('GMT');
$myDateTime = new DateTime('2016-03-21 13:14', $gmtTimezone);
$offset = $userTimezone->getOffset($myDateTime);
$myInterval=DateInterval::createFromDateString((string)$offset . 'seconds');
$myDateTime->add($myInterval);
$result = $myDateTime->format('Y-m-d H:i:s');
Echo $result;

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

So that’s how we handle PHP timezones at Server Density. What’s your approach?

Free eBook: 4 Steps to Successful DevOps

This eBook will show you how we i) hacked our on-call rotation to increase code resilience, ii) broke our infrastructure, on purpose, to debug quicker and increase uptime, and iii) borrowed practices from the healthcare and aviation industry, to reduce complexity, stress and fatigue. And speaking of stress and fatigue, we’ve devoted an entire chapter on how we placed humans at the centre of Ops, in order to increase their productivity and boost the uptime of the systems they manage. What are you waiting for, download your free copy now.

What infrastructure do you currently work with?

Help us speak your language. What is your primary tech stack?

  • 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

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

  • Blake

    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.

  • Pingback: Automatic timezone conversion in JavaScript « Boxed Ice Blog()

  • Josh

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

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

      • Josh

        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

      • Sam

        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.

      • Michael

        Hey, just thought I would add that according to the docs setTimezone should indeed cause a conversion to occur. Tthe examples shown appear to indicate this (even though the textual description does not explicit state it):


        $date = new DateTime('2000-01-01', new DateTimeZone('Pacific/Nauru'));
        echo $date->format('Y-m-d H:i:sP') . "n";

        $date->setTimezone(new DateTimeZone('Pacific/Chatham'));
        echo $date->format('Y-m-d H:i:sP') . "n";

        Output:


        2000-01-01 00:00:00+12:00
        2000-01-01 01:45:00+13:45

    • This worked fine for me.

      Thanks for a good tutorial, pointed me in the right direction after going mad with bugs in PEAR Date.

      I’ve written up a simple function based on Josh’s script…

      http://richardwillia.ms/blog/2011/04/time-zone-conversion-using-datetime-class/

  • I. Zuiff

    Are you having a problem using
    date_default_timezone_set() ?

    It seems to be working for me. My server is in Arizona and I am on the east-coast.

    output: Wed,March 24, 2010, 12:27:17 PM EDT

  • I. Zuiff

    here’s the code:

    Are you having a problem using
    date_default_timezone_set() ?

    It seems to be working for me. My server is in Arizona and I am on the east-coast.

    date_default_timezone_set(‘America/New_York’);
    echo date(‘D,F j, Y, h:i:s A T’);

    output: Wed,March 24, 2010, 12:27:17 PM EDT

  • Chris

    This helped me a lot, thanks!

  • I face a similar challenge. I need to convert a user’s chosen time to my server’s time. e.g. I have a form which a user can select a date, time, and their timezone. Upon submission of the form, I want to timestamp the file with my timezone’s time. My server is in KS, say a user in CA chooses July 1, 2010 8:00 AM Pacific timezone…I want my server timestamp the file July 1, 2010 10:00 AM Central time. Help?

  • WayFarer

    And timezone conversion from server’s timezone to user’s in one line (convinuent for templates):

    echo date_create($userDate)->setTimezone(new DateTimeZone($userTimezone))->format(‘Y-m-d H:i’);

    or from one timezone to another:
    echo date_create($userDate, new DateTimeZone($userTimezoneFrom))->setTimezone(new DateTimeZone($userTimezoneTo))->format(‘Y-m-d H:i’);

  • Pingback: Automatic timezone conversion with JavaScript « Boxed Ice Blog()

  • That’s exactly the code i was looking for so long! Thanks!

  • Skibbler Elf

    This post has helped a ton. It does help to know your user’s timezone…then you can figure the offset and store the correct time (most likely UTC) in your dB.

    Ciao

  • Very useful, thanks, just put Timezone handling into our open source project :-)

  • Jeff

    I see a lot of people have already commented more or less what I was going to say — it’s much easier to use the setTimezone() method. However I’d like to point out that it’s not only easier, it’s really the way you have to do it (if you want it to work). First off, the approach your suggesting only appears to work because you happen to be calculating your offset against a DateTime object which happens to be using the GMT timezone. getOffset() always returns the offset from GMT, regardless of the timezone set on the DateTime object you use. In fact, the only reason you need the DateTime object is because that world time is used to determine what the offset is — The offset changes for TimeZones which follow daylight savings time and depending on what time of the year it is, the offset will change.
    Mostly I want to point out though that your method of changing the time using the offset will leave you with a new DateTime object that has the wrong time. Or rather it will have the right time, but the wrong TimeZone. Worse it won’t necessarily be the right time — if you happen to offset the time across a clock change (spring forward, fall back) you’ll end up with the wrong time and the wrong timezone.
    Here’s what I’m getting at. Suppose the following time
    date_create(‘2011-03-13T01:30’, timezone_open(‘America/Chicago’));
    That’s just 30 minutes before the clocks get turned forward. If you add an hour with the following line
    $sometime->add(date_interval_create_from_date_string(‘1 hour’));
    The resulting time is actually 3:30am because the hour from 2-3am doesn’t exist. Also, the clocks in the US all change at 2am based on local time, so strangely enough, in the fall you end up with a situation where 2:30am in Chicago is actually the second 2:30am in New York.
    If you use the setTimezone method though you’ll find that your times always change consistently because under the covers they’re always tracking UTC (GMT).

  • works for me.

    setTimezone(new DateTimeZone(‘Asia/Manila’));
    echo $myDateTime->format(‘Y-m-d H:i’);
    ?>

  • Gary

    Thanks for this. I found the information and technique very useful.

  • Vibha

    Hi,

    Thanx for the script,

    I am showing server time and client time both on a page, used different variables. Script works fine in Firefox and Chrome, but in Safari, IE9 and Opera, it is not working, either or both times are not showing.
    Please help

  • Nick

    Thanks for this

  • Pingback: Handling timezone conversion with PHP DateTime « Boxed Ice Blog - Daniel Hansson()

  • Pingback: Quora()

  • Great post!

Articles you care about. Delivered.

Maybe another time