Page 1 of 1

How to work with Time Zones in php

Posted: Mon Mar 01, 2010 12:23 am
by Neo
Let's say you are developing an online application that involves users selecting or viewing times. One problem that you'll need to address is time zones. If your application is going to be used by users all over the world, you'll want to adjust the times to be in their local time zone to prevent confusion. Fortunately, PHP 5.2.0+ greatly simplifies this process with the DateTime and DateTimeZone classes.

The DateTime class provides all the date and time handling functionality, while the DateTimeZone provides DateTime objects with all the time zone information. We just need to provide DateTimeZone with the time zone in the Area/Location format (for example, the time zone in which this server is located is America/Los_Angeles), and it will return an object representing that time zone. We can pass this to a DateTime object to convert any time into this time zone.

Here is an overview of the steps required to accomplish this:
  • Collect the time zone from the user and store in Area/Location format
  • Create a DateTimeZone object using the provided time zone
  • Create a DateTime object, providing it the time in the local time zone
  • Convert the DateTime object to the time zone created in step 1
  • Output the time from the DateTime object
One thing that we can do to avoid confusion is to store all times as a Unix timestamp. Unix timestamps are not affected by time zones since they always represent a time in UTC time. Also, PHP 5.3.0 (still a release candidate as of this tutorial) allows you to provide a timestamp directly to the DateTime class, so I will show the both code for PHP 5.2 and simplified code for PHP 5.3.

The first step is important, because we need to know the time zone of the user before we can think about converting times to their time zone. There are some methods to automatically predict this, but they tend to be unreliable and/or expensive (such as commercial GeoIP databases). So the best method is to simply ask the user where they are. (You can combine these methods by defaulting to the most likely setting when asking, but I won't go into that now).

Here is the code to create a select list of time zones and showing the user a more friendly name representing each zone:

Code: Select all

<?php
if(isset($_POST['tz'])) {
	// store the selected value for future use in DateTimeZone
	$_SESSION['tz'] = $_POST['tz'];
}
// create an array listing the time zones
$zonelist = array('Kwajalein' => '(GMT-12:00) International Date Line West',
		'Pacific/Midway' => '(GMT-11:00) Midway Island',
		'Pacific/Samoa' => '(GMT-11:00) Samoa',
		'Pacific/Honolulu' => '(GMT-10:00) Hawaii',
		'America/Anchorage' => '(GMT-09:00) Alaska',
		'America/Los_Angeles' => '(GMT-08:00) Pacific Time (US & Canada)',
		'America/Tijuana' => '(GMT-08:00) Tijuana, Baja California',
		'America/Denver' => '(GMT-07:00) Mountain Time (US & Canada)',
		'America/Chihuahua' => '(GMT-07:00) Chihuahua',
		'America/Mazatlan' => '(GMT-07:00) Mazatlan',
		'America/Phoenix' => '(GMT-07:00) Arizona',
		'America/Regina' => '(GMT-06:00) Saskatchewan',
		'America/Tegucigalpa' => '(GMT-06:00) Central America',
		'America/Chicago' => '(GMT-06:00) Central Time (US & Canada)',
		'America/Mexico_City' => '(GMT-06:00) Mexico City',
		'America/Monterrey' => '(GMT-06:00) Monterrey',
		'America/New_York' => '(GMT-05:00) Eastern Time (US & Canada)',
		'America/Bogota' => '(GMT-05:00) Bogota',
		'America/Lima' => '(GMT-05:00) Lima',
		'America/Rio_Branco' => '(GMT-05:00) Rio Branco',
		'America/Indiana/Indianapolis' => '(GMT-05:00) Indiana (East)',
		'America/Caracas' => '(GMT-04:30) Caracas',
		'America/Halifax' => '(GMT-04:00) Atlantic Time (Canada)',
		'America/Manaus' => '(GMT-04:00) Manaus',
		'America/Santiago' => '(GMT-04:00) Santiago',
		'America/La_Paz' => '(GMT-04:00) La Paz',
		'America/St_Johns' => '(GMT-03:30) Newfoundland',
		'America/Argentina/Buenos_Aires' => '(GMT-03:00) Georgetown',
		'America/Sao_Paulo' => '(GMT-03:00) Brasilia',
		'America/Godthab' => '(GMT-03:00) Greenland',
		'America/Montevideo' => '(GMT-03:00) Montevideo',
		'Atlantic/South_Georgia' => '(GMT-02:00) Mid-Atlantic',
		'Atlantic/Azores' => '(GMT-01:00) Azores',
		'Atlantic/Cape_Verde' => '(GMT-01:00) Cape Verde Is.',
		'Europe/Dublin' => '(GMT) Dublin',
		'Europe/Lisbon' => '(GMT) Lisbon',
		'Europe/London' => '(GMT) London',
		'Africa/Monrovia' => '(GMT) Monrovia',
		'Atlantic/Reykjavik' => '(GMT) Reykjavik',
		'Africa/Casablanca' => '(GMT) Casablanca',
		'Europe/Belgrade' => '(GMT+01:00) Belgrade',
		'Europe/Bratislava' => '(GMT+01:00) Bratislava',
		'Europe/Budapest' => '(GMT+01:00) Budapest',
		'Europe/Ljubljana' => '(GMT+01:00) Ljubljana',
		'Europe/Prague' => '(GMT+01:00) Prague',
		'Europe/Sarajevo' => '(GMT+01:00) Sarajevo',
		'Europe/Skopje' => '(GMT+01:00) Skopje',
		'Europe/Warsaw' => '(GMT+01:00) Warsaw',
		'Europe/Zagreb' => '(GMT+01:00) Zagreb',
		'Europe/Brussels' => '(GMT+01:00) Brussels',
		'Europe/Copenhagen' => '(GMT+01:00) Copenhagen',
		'Europe/Madrid' => '(GMT+01:00) Madrid',
		'Europe/Paris' => '(GMT+01:00) Paris',
		'Africa/Algiers' => '(GMT+01:00) West Central Africa',
		'Europe/Amsterdam' => '(GMT+01:00) Amsterdam',
		'Europe/Berlin' => '(GMT+01:00) Berlin',
		'Europe/Rome' => '(GMT+01:00) Rome',
		'Europe/Stockholm' => '(GMT+01:00) Stockholm',
		'Europe/Vienna' => '(GMT+01:00) Vienna',
		'Europe/Minsk' => '(GMT+02:00) Minsk',
		'Africa/Cairo' => '(GMT+02:00) Cairo',
		'Europe/Helsinki' => '(GMT+02:00) Helsinki',
		'Europe/Riga' => '(GMT+02:00) Riga',
		'Europe/Sofia' => '(GMT+02:00) Sofia',
		'Europe/Tallinn' => '(GMT+02:00) Tallinn',
		'Europe/Vilnius' => '(GMT+02:00) Vilnius',
		'Europe/Athens' => '(GMT+02:00) Athens',
		'Europe/Bucharest' => '(GMT+02:00) Bucharest',
		'Europe/Istanbul' => '(GMT+02:00) Istanbul',
		'Asia/Jerusalem' => '(GMT+02:00) Jerusalem',
		'Asia/Amman' => '(GMT+02:00) Amman',
		'Asia/Beirut' => '(GMT+02:00) Beirut',
		'Africa/Windhoek' => '(GMT+02:00) Windhoek',
		'Africa/Harare' => '(GMT+02:00) Harare',
		'Asia/Kuwait' => '(GMT+03:00) Kuwait',
		'Asia/Riyadh' => '(GMT+03:00) Riyadh',
		'Asia/Baghdad' => '(GMT+03:00) Baghdad',
		'Africa/Nairobi' => '(GMT+03:00) Nairobi',
		'Asia/Tbilisi' => '(GMT+03:00) Tbilisi',
		'Europe/Moscow' => '(GMT+03:00) Moscow',
		'Europe/Volgograd' => '(GMT+03:00) Volgograd',
		'Asia/Tehran' => '(GMT+03:30) Tehran',
		'Asia/Muscat' => '(GMT+04:00) Muscat',
		'Asia/Baku' => '(GMT+04:00) Baku',
		'Asia/Yerevan' => '(GMT+04:00) Yerevan',
		'Asia/Yekaterinburg' => '(GMT+05:00) Ekaterinburg',
		'Asia/Karachi' => '(GMT+05:00) Karachi',
		'Asia/Tashkent' => '(GMT+05:00) Tashkent',
		'Asia/Kolkata' => '(GMT+05:30) Calcutta',
		'Asia/Colombo' => '(GMT+05:30) Sri Jayawardenepura',
		'Asia/Katmandu' => '(GMT+05:45) Kathmandu',
		'Asia/Dhaka' => '(GMT+06:00) Dhaka',
		'Asia/Almaty' => '(GMT+06:00) Almaty',
		'Asia/Novosibirsk' => '(GMT+06:00) Novosibirsk',
		'Asia/Rangoon' => '(GMT+06:30) Yangon (Rangoon)',
		'Asia/Krasnoyarsk' => '(GMT+07:00) Krasnoyarsk',
		'Asia/Bangkok' => '(GMT+07:00) Bangkok',
		'Asia/Jakarta' => '(GMT+07:00) Jakarta',
		'Asia/Brunei' => '(GMT+08:00) Beijing',
		'Asia/Chongqing' => '(GMT+08:00) Chongqing',
		'Asia/Hong_Kong' => '(GMT+08:00) Hong Kong',
		'Asia/Urumqi' => '(GMT+08:00) Urumqi',
		'Asia/Irkutsk' => '(GMT+08:00) Irkutsk',
		'Asia/Ulaanbaatar' => '(GMT+08:00) Ulaan Bataar',
		'Asia/Kuala_Lumpur' => '(GMT+08:00) Kuala Lumpur',
		'Asia/Singapore' => '(GMT+08:00) Singapore',
		'Asia/Taipei' => '(GMT+08:00) Taipei',
		'Australia/Perth' => '(GMT+08:00) Perth',
		'Asia/Seoul' => '(GMT+09:00) Seoul',
		'Asia/Tokyo' => '(GMT+09:00) Tokyo',
		'Asia/Yakutsk' => '(GMT+09:00) Yakutsk',
		'Australia/Darwin' => '(GMT+09:30) Darwin',
		'Australia/Adelaide' => '(GMT+09:30) Adelaide',
		'Australia/Canberra' => '(GMT+10:00) Canberra',
		'Australia/Melbourne' => '(GMT+10:00) Melbourne',
		'Australia/Sydney' => '(GMT+10:00) Sydney',
		'Australia/Brisbane' => '(GMT+10:00) Brisbane',
		'Australia/Hobart' => '(GMT+10:00) Hobart',
		'Asia/Vladivostok' => '(GMT+10:00) Vladivostok',
		'Pacific/Guam' => '(GMT+10:00) Guam',
		'Pacific/Port_Moresby' => '(GMT+10:00) Port Moresby',
		'Asia/Magadan' => '(GMT+11:00) Magadan',
		'Pacific/Fiji' => '(GMT+12:00) Fiji',
		'Asia/Kamchatka' => '(GMT+12:00) Kamchatka',
		'Pacific/Auckland' => '(GMT+12:00) Auckland',
		'Pacific/Tongatapu' => '(GMT+13:00) Nukualofa');
?>
<form method="post">
	<select name="tz">
	<option value="Pacific/Honolulu">(GMT-10:00) Hawaii</option>
	<option value="America/Anchorage">(GMT-09:00) Alaska</option>
	<option value="America/Los_Angeles">(GMT-08:00) Pacific Time (US & Canada)</option>
	<option value="America/Phoenix">(GMT-07:00) Arizona</option>
	<option value="America/Denver">(GMT-07:00) Mountain Time (US & Canada)</option>
	<option value="America/Chicago">(GMT-06:00) Central Time (US & Canada)</option>
	<option value="America/New_York">(GMT-05:00) Eastern Time (US & Canada)</option>
	<option value="America/Indiana/Indianapolis">(GMT-05:00) Indiana (East)</option>
	<option disabled="disabled">&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</option>
<?php
foreach($zonelist as $key => $value) {
	echo '		<option value="' . $key . '">' . $value . '</option>' . "\n";
}
?>
	</select>
	<input type="submit" name="submit" value="Set" />
</form>
This can be included as part of a user registration form, and the setting can be stored using your preferred method (database, cookie, etc). Here, I listed all the US time zones first for convenience if your application is based in the US.

So now that we have the needed information from the user, we can start with the conversion code. There are two different conversions we might need to do: Timestamp -> User's Time and User's Time -> Timestamp. This is for displaying time to the user and collecting time from the user, respectively.

Converting a timestamp to the user's time:
PHP 5.2:

Code: Select all

// timestamp to convert (just an example)
$timestamp = 1240349566;
 
// set this to the time zone provided by the user
$tz = $_SESSION['tz'];
 
// create the DateTimeZone object for later
$dtzone = new DateTimeZone($tz);
 
// first convert the timestamp into a string representing the local time
$time = date('r', $timestamp);
 
// now create the DateTime object for this time
$dtime = new DateTime($time);
 
// convert this to the user's timezone using the DateTimeZone object
$dtime->setTimeZone($dtzone);
 
// print the time using your preferred format
$time = $dtime->format('g:i A m/d/y');  

PHP 5.3:

Code: Select all

// timestamp to convert (just an example)
$timestamp = 1240349566;
 
// set this to the time zone provided by the user
$tz = $_SESSION['tz'];
 
// create the DateTimeZone object for later
$dtzone = new DateTimeZone($tz);
 
// create a DateTime object
$dtime = new DateTime();
 
// set it to the timestamp (PHP >= 5.3.0)
$dtime->setTimestamp($timestamp);
 
// convert this to the user's timezone using the DateTimeZone object
$dtime->setTimeZone($dtzone);
 
// print the time using your preferred format
$time = $dtime->format('g:i A m/d/y');  

Converting user's time to a timestamp:

Code: Select all

// time to convert (just an example)
$time = 'Tuesday, April 21, 2009 2:32:46 PM';
 
// set this to the time zone provided by the user
$tz = $_SESSION['tz'];
 
// create the DateTimeZone object for later
$dtzone = new DateTimeZone($tz);
 
// now create the DateTime object for this time and user time zone
$dtime = new DateTime($time, $dtzone);
 
// print the timestamp
$timestamp = $dtime->format('U');  
So there you have it. Those few lines of code is all it takes to convert time zones in PHP.