1

I need to set timezone in php. I have the offset in seconds of the new timezone and I want to set it using date_default_timezone_set. I have no probelm converting the seconds into offset like +0200 but I don't think it's enough.
From what I understood after reading the manual, is that I need to give as parameter something like America\New_York. Is there a way of converting the offset into a specific location?

user1908466
  • 503
  • 1
  • 8
  • 17

4 Answers4

4

You actually can't reliably do this. Each named time zone has multiple unique properties that change over time. The current offset is only part of the information. You also have to consider how daylight saving time applies, which could apply differently (or not at all) per time zone. You also have to consider how a single time zone can have a history of different values as they may have changed their offsets or DST rules many times over.

You should also consider that at any given time, several different time zones will be using the same offset. If you just pick one at random, you are ignoring a lot of important details!

Please read the timezone tag wiki.

The other answers here may return a value, but they are all based on false assumptions. A time zone simply can't be represented by a number alone.

Community
  • 1
  • 1
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Very good point, and thanks for the link to the wiki article. I've now improved my solution, to take into account the historical changes in DST. – TachyonVortex Feb 26 '14 at 14:11
3

You can search the output of DateTimeZone::listAbbreviations() for timezones which match your offset:

function convertTimezoneOffsetToId($offsetSeconds)
{
    $ids = array();

    foreach (DateTimeZone::listAbbreviations() as $abbrev) {
        foreach ($abbrev as $zone) {
            if ($zone['offset'] == $offsetSeconds) {
                $ids[] = $zone['timezone_id'];
            }
        }
    }

    return array_unique($ids);
}

For example:

convertTimezoneOffsetToId(5040);

returns:

Array
(
    [0] => Europe/Vilnius
    [1] => Europe/Warsaw
)

Edit

As Matt points out, this function doesn't take into account the historical changes in DST.

So here's an improved function which does:

function convertTimezoneOffsetToId($offsetSeconds, $unixTimestamp)
{
    $ids = array();

    foreach (DateTimeZone::listIdentifiers() as $id) {
        $dtz   = new DateTimeZone($id);
        $trans = $dtz->getTransitions($unixTimestamp, $unixTimestamp);
        if ($trans[0]['offset'] == $offsetSeconds) {
            $ids[] = $id;
        }    
    }

    return array_unique($ids);
}

It uses DateTimeZone::getTransitions() to get each timezone's offset at a particular moment in history.

For example:

convertTimezoneOffsetToId(19800, time());

returns a list of timezones which have an offset of 19800 seconds right now:

Array
(
    [0] => Asia/Colombo
    [1] => Asia/Kolkata
)

and:

convertTimezoneOffsetToId(19800, gmmktime(0, 0, 0, 1, 1, 2000));

returns a list of timezones which had an offset of 19800 seconds on 1st Jan 2000:

Array
(
    [0] => Asia/Kolkata
)

Notice that Asia/Colombo has disappeared, because on 1st Jan 2000 its offset was 21600 seconds.

Community
  • 1
  • 1
TachyonVortex
  • 8,242
  • 3
  • 48
  • 63
0

There is, yes. I'll just quote this excellent answer by uınbɐɥs:

$timezone = '+2:00';
$timezone = preg_replace('/[^0-9]/', '', $timezone) * 36;
$timezone_name = timezone_name_from_abbr(null, $timezone, true); //= Europe/Paris
date_default_timezone_set($timezone_name);

I'd encourage you to take a look at his answer in detail, since there's a bug that you might need to take into account.

Community
  • 1
  • 1
Lasse
  • 1,414
  • 11
  • 19
0

EDITED (original answer completely scrapped)

One approach would be to create a map of offset hour strings (such as +0800) to a timezone name that PHP recognizes. It's not 1-to-1, since there are multiple names for each offset. But since you care about the offset and not the name, then your mapping can just choose any of the available timezone names for any given offset.

Since there aren't hundreds and hundreds of timezones, you really only end up with an array of about 35 entries (there are a few timezones at the 30-minute or even 45-minute mark).

Here is a code sample that pretty much does what you need:

$timezones = array(
  '-1100' =>  'Pacific/Midway',
  '-1000' =>  'US/Hawaii',
  '-0900' =>  'US/Alaska',
  '-0800' =>  'US/Pacific',
  '-0700' =>  'US/Arizona',
  '-0600' =>  'America/Mexico_City',
  '-0500' =>  'US/Eastern',
  '-0430' =>  'America/Caracas',
  '-0400' =>  'Canada/Atlantic',
  '-0330' =>  'Canada/Newfoundland',
  '-0300' =>  'America/Buenos_Aires',
  '-0200' =>  'Atlantic/Stanley',
  '-0100' =>  'Atlantic/Azores',
  '-0100' =>  'Atlantic/Cape_Verde',
  '+0000' =>  'Europe/London',
  '+0100' =>  'Europe/Amsterdam',
  '+0200' =>  'Europe/Athens',
  '+0300' =>  'Asia/Baghdad',
  '+0330' =>  'Asia/Tehran',
  '+0400' =>  'Europe/Moscow',
  '+0430' =>  'Asia/Kabul',
  '+0500' =>  'Asia/Karachi',
  '+0530' =>  'Asia/Kolkata',
  '+0545' =>  'Asia/Kathmandu',
  '+0600' =>  'Asia/Yekaterinburg',
  '+0700' =>  'Asia/Novosibirsk',
  '+0800' =>  'Asia/Krasnoyarsk',
  '+0800' =>  'Asia/Urumqi',
  '+0900' =>  'Asia/Irkutsk',
  '+0930' =>  'Australia/Adelaide',
  '+1000' =>  'Asia/Yakutsk',
  '+1000' =>  'Australia/Sydney',
  '+1100' =>  'Asia/Vladivostok',
  '+1200' =>  'Asia/Magadan'
);

for ($offset_hours = -11; $offset_hours <= 12; $offset_hours++) {
  // Convert to a timezone string.  For example, 8 => +0800
  $offset_string = sprintf("%+03d", $offset_hours) . "00";
  date_default_timezone_set($timezones[$offset_string]);

  $dt = new DateTime();
  print "OFFSET: $offset_hours hours ($offset_string)\n";
  print $dt->format(DATE_RFC822) . "\n";
  print "\n";
}

The for loop just demonstrates the setting of almost all the different timezones based on an iterating offset (I excluded the 30-minute and 45-minute mark timezones, for simplicity).

Here is an excerpt of the output from running the above code:

OFFSET: -11 hours (-1100)
Tue, 25 Feb 14 04:24:13 -1100

OFFSET: -10 hours (-1000)
Tue, 25 Feb 14 05:24:13 -1000

OFFSET: -9 hours (-0900)
Tue, 25 Feb 14 06:24:13 -0900

...
...
...

OFFSET: -1 hours (-0100)
Tue, 25 Feb 14 14:24:13 -0100

OFFSET: 0 hours (+0000)
Tue, 25 Feb 14 15:24:13 +0000

OFFSET: 1 hours (+0100)
Tue, 25 Feb 14 16:24:13 +0100

...
...
...

OFFSET: 10 hours (+1000)
Wed, 26 Feb 14 02:24:13 +1100

OFFSET: 11 hours (+1100)
Wed, 26 Feb 14 02:24:13 +1100

OFFSET: 12 hours (+1200)
Wed, 26 Feb 14 03:24:13 +1200

Credit goes to @Eugene Manuilov for his StackOverflow answer regarding PHP Timezones, as it meant I did not have to write up the array entirely from scratch.

Community
  • 1
  • 1
Alvin S. Lee
  • 4,984
  • 30
  • 34