3.12.1 Problem
You need to calculate times in different time
zones. For example, you want to give users information adjusted to their local
time, not the local time of your server.
3.12.2 Solution
For simple calculations, you can explicitly add or subtract the
offsets between two time zones:
// If local time is EST $time_parts = localtime(); // California (PST) is three hours earlier $california_time_parts = localtime(time() - 3 * 3600);
On Unix-based systems, if you don't know the offsets between
time zones, just set the TZ environment variable to your target time
zone:
putenv('TZ=PST8PDT');
$california_time_parts = localtime();
3.12.3 Discussion
Before we sink too deeply into the ins and outs of time zones,
we want to pass along the disclaimer that the U.S. Naval
Observatory offers at http://tycho.usno.navy.mil/tzones.html. Namely, official
worldwide time-zone information is somewhat fragile "because nations are
sovereign powers that can and do change their timekeeping systems as they see
fit." So, remembering that we are at the mercy of the vagaries of international
relations, here are some ways to cope with Earth's many time zones.
For a relatively
simple treatment of offsets between time zones, use an array in your program
that has the various time zones' offsets from UTC. Once
you determine what time zone your user is in, just add that offset to the
appropriate UTC time and the functions that print out UTC time (e.g., gmdate(
), gmstrftime( )) can print out the correct adjusted time.
// Find the current time $now = time(); // California is 8 hours behind UTC $now += $pc_timezones['PST']; // Use gmdate() or gmstrftime() to print California-appropriate time print gmstrftime('%c',$now);
// From Perl's Time::Timezone $pc_timezones = array( 'GMT' => 0, // Greenwich Mean 'UTC' => 0, // Universal (Coordinated) 'WET' => 0, // Western European 'WAT' => -1*3600, // West Africa 'AT' => -2*3600, // Azores 'NFT' => -3*3600-1800, // Newfoundland 'AST' => -4*3600, // Atlantic Standard 'EST' => -5*3600, // Eastern Standard 'CST' => -6*3600, // Central Standard 'MST' => -7*3600, // Mountain Standard 'PST' => -8*3600, // Pacific Standard 'YST' => -9*3600, // Yukon Standard 'HST' => -10*3600, // Hawaii Standard 'CAT' => -10*3600, // Central Alaska 'AHST' => -10*3600, // Alaska-Hawaii Standard 'NT' => -11*3600, // Nome 'IDLW' => -12*3600, // International Date Line West 'CET' => +1*3600, // Central European 'MET' => +1*3600, // Middle European 'MEWT' => +1*3600, // Middle European Winter 'SWT' => +1*3600, // Swedish Winter 'FWT' => +1*3600, // French Winter 'EET' => +2*3600, // Eastern Europe, USSR Zone 1 'BT' => +3*3600, // Baghdad, USSR Zone 2 'IT' => +3*3600+1800, // Iran 'ZP4' => +4*3600, // USSR Zone 3 'ZP5' => +5*3600, // USSR Zone 4 'IST' => +5*3600+1800, // Indian Standard 'ZP6' => +6*3600, // USSR Zone 5 'SST' => +7*3600, // South Sumatra, USSR Zone 6 'WAST' => +7*3600, // West Australian Standard 'JT' => +7*3600+1800, // Java 'CCT' => +8*3600, // China Coast, USSR Zone 7 'JST' => +9*3600, // Japan Standard, USSR Zone 8 'CAST' => +9*3600+1800, // Central Australian Standard 'EAST' => +10*3600, // Eastern Australian Standard 'GST' => +10*3600, // Guam Standard, USSR Zone 9 'NZT' => +12*3600, // New Zealand 'NZST' => +12*3600, // New Zealand Standard 'IDLE' => +12*3600 // International Date Line East );
On Unix systems, you can use the zoneinfo library to do the conversions. This makes your code more
compact and also transparently handles DST, as discussed in Section 3.13.
To take advantage of zoneinfo in
PHP, do all your internal date math with epoch timestamps. Generate them from
time parts with the pc_mktime( ) function shown
in Section 3-2.
Example 3-2. pc_mktime( )
function pc_mktime($tz,$hr,$min,$sec,$mon,$day,$yr) {
putenv("TZ=$tz");
$a = mktime($hr,$min,$sec,$mon,$day,$yr);
putenv('TZ=EST5EDT'); // change EST5EDT to your server's time zone!
return $a;
}
Calling putenv( ) before mktime( ) fools the system functions
mktime( ) uses into thinking they're in a different time zone. After
the call to mktime( ), the correct time zone has to be restored. On the
East Coast of the United States, that's EST5EDT. Change this to the
appropriate value for your computer's location (see Table 3-5).
Time parts are turned into epoch timestamps by pc_mktime(
). Its counterpart, to turn epoch timestamps into formatted time strings
and time parts, is pc_strftime( ), shown in Example 3-3.
Example 3-3. pc_strftime( )
function pc_strftime($tz,$format,$timestamp) {
putenv("TZ=$tz");
$a = strftime($format,$timestamp);
putenv('TZ=EST5EDT'); // change EST5EDT to your server's time zone!
return $a;
}
This example uses the same system-function-fooling
pc_mktime( ) does to get the right results from strftime( ).
The great thing about these functions is that you don't have to
worry about the offsets from UTC of different time zones, whether DST is in effect, or any other irregularities of time-zone
differences. You just set the appropriate zone and let your system's libraries
do the rest.
Note that the value of the $tz variable in both these
functions should not be a time-zone name but a zoneinfo zone. zoneinfo zones are more
specific than time zones, because they correspond to particular places. Table 3-5 contains mappings for
appropriate zoneinfo zones for some UTC offsets.
The last column indicates whether the zone observes DST.
UTC offset (hours)
|
UTC offset (seconds)
|
zoneinfo zone
|
DST?
|
|---|---|---|---|
-12
|
-43200
|
Etc/GMT+12
|
No
|
-11
|
-39600
|
Pacific/Midway
|
No
|
-10
|
-36000
|
US/Aleutian
|
Yes
|
-10
|
-36000
|
Pacific/Honolulu
|
No
|
-9
|
-32400
|
America/Anchorage
|
Yes
|
-9
|
-32400
|
Etc/GMT+9
|
No
|
-8
|
-28800
|
PST8PDT
|
Yes
|
-8
|
-28800
|
America/Dawson_Creek
|
No
|
-7
|
-25200
|
MST7MDT
|
Yes
|
-7
|
-25200
|
MST
|
No
|
-6
|
-21600
|
CST6CDT
|
Yes
|
-6
|
-21600
|
Canada/Saskatchewan
|
No
|
-5
|
-18000
|
EST5EDT
|
Yes
|
-5
|
-18000
|
EST
|
No
|
-4
|
-14400
|
America/Halifax
|
Yes
|
-4
|
-14400
|
America/Puerto_Rico
|
No
|
-3.5
|
-12600
|
America/St_Johns
|
Yes
|
-3
|
-10800
|
America/Buenos_Aires
|
No
|
0
|
0
|
Europe/London
|
Yes
|
0
|
0
|
GMT
|
No
|
1
|
3600
|
CET
|
Yes
|
1
|
3600
|
GMT-1
|
No
|
2
|
7200
|
EET
|
No
|
2
|
7200
|
GMT-2
|
No
|
3
|
10800
|
Asia/Baghdad
|
Yes
|
3
|
10800
|
GMT-3
|
No
|
3.5
|
12600
|
Asia/Tehran
|
Yes
|
4
|
14400
|
Asia/Dubai
|
No
|
4
|
14400
|
Asia/Baku
|
Yes
|
4.5
|
16200
|
Asia/Kabul
|
No
|
5
|
18000
|
Asia/Tashkent
|
No
|
5.5
|
19800
|
Asia/Calcutta
|
No
|
5.75
|
20700
|
Asia/Katmandu
|
No
|
6
|
21600
|
Asia/Novosibirsk
|
Yes
|
6
|
21600
|
Etc/GMT-6
|
No
|
6.5
|
23400
|
Asia/Rangoon
|
No
|
7
|
25200
|
Asia/Jakarta
|
No
|
8
|
28800
|
Hongkong
|
No
|
9
|
32400
|
Japan
|
No
|
9.5
|
34200
|
Australia/Darwin
|
No
|
10
|
36000
|
Australia/Sydney
|
Yes
|
10
|
36000
|
Pacific/Guam
|
No
|
12
|
43200
|
Etc/GMT-13
|
No
|
12
|
43200
|
Pacific/Auckland
|
Yes
|
Countries around the world don't begin and end DST observance
on the same days or at the same times. To calculate time appropriately for an
international DST-observing location, pick a zoneinfo zone that matches your desired location as
specifically as possible.