Search code examples
phpgeolocationtimezonetimezone-offset

Convert UTC offset to timezone or date


A head scratcher for you.

I am grabbing geo IP data from IPInfoDB's API and it returns a timezone offset from UTC including DST (if currently reflected).

For example, I live in EST (-5) and currently it's DST, so the geo IP API returns (-04:00) as the offset.

This is wonderful since DST is a freaking headache. But to my surprise, it caused another headache.

I load this data in PHP to be passed via AJAX to the application. I would like to have the live local time of the IP address on the app.

I have that all set perfectly, but I am going crazy trying to figure out how to set the PHP timezone to match the offset so I can just grab the current hours date('H'); and minutes date('i'); to pass to via AJAX.

I am unsure if there is a specific function that can just give me the current hours and minutes based on that offset or if there is a practical way to set the timezone based on the offset (which will have DST already applied if is in effect).

I've been searching and searching Google to find an answer to this, but what I am doing is more specific since DST is already applied.

I found one function on PHP.net that seems to do the trick (it works for my timezone and returns the correct time) although for other timezones such as PST, it's returning 1 hour later than it should be even though the offset is correct (-07:00 with DST).

The timezone returned from the function is Chile/EasterIsland which I have a feeling is the cause. If I could, I would make this only work for the USA, but I do need it to be worldwide.

This is the function I have now. Please excuse the extremely messy code. I have been playing around with tons of things over the last few hours trying to figure out a solution.

Most of the functionality was found online.

function offsetToTZ($offset) {
switch((string) $offset) {
    case '-04:30' : return 'America/Caracas'; break;
    case '-03:30' : return 'Canada/Newfoundland'; break;
    case '+03:30' : return 'Asia/Tehran'; break;
    case '+04:30' : return 'Asia/Kabul'; break;
    case '+05:30' : return 'Asia/Kolkata'; break;
    case '+05:45' : return 'Asia/Kathmandu'; break;
    case '+09:30' : return 'Australia/Darwin'; break;
}
$offset = (int) str_replace(array('0',0,':00',00,'30',30,'45',45,':','+'),'', (string) $offset);

$offset = $offset*60*60;
$abbrarray = timezone_abbreviations_list(); 
foreach ($abbrarray as $abbr) { 
    foreach($abbr as $city) { 
        if($city['offset'] == $offset) { 
            return $city['timezone_id'];
        }
    }
}
return false; 
}

I included the switch/case for certain timezones that are :30 and :45 out there. There may be a way to include that also without the need of the switch/case.

NOTE: The offsets are always returned as such +00:00 or -00:00 from the geo IP API.

I would appreciate any help or a point in the right direction. I'm not very novice with PHP, but offsets are a new story for me. Thanks!


Solution

  • It can be done quite simply, by turning the offset into seconds and passing it to timezone_name_from_abbr:

    <?php
    $offset = '-7:00';
    
    // Calculate seconds from offset
    list($hours, $minutes) = explode(':', $offset);
    $seconds = $hours * 60 * 60 + $minutes * 60;
    // Get timezone name from seconds
    $tz = timezone_name_from_abbr('', $seconds, 1);
    // Workaround for bug #44780
    if($tz === false) $tz = timezone_name_from_abbr('', $seconds, 0);
    // Set timezone
    date_default_timezone_set($tz);
    
    echo $tz . ': ' . date('r');
    

    Demo

    The third parameter of timezone_name_from_abbr controls whether to adjust for daylight saving time or not.

    Bug #44780:

    timezone_name_from_abbr() will return false on some time zone offsets. In particular - Hawaii, which has a -10 from GMT offset, -36000 seconds.

    References: