Handling timezone conversion with PHP DateTime
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.
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:
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).
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?