2012-08-05 148 views
13

您的頭部劃痕。將UTC偏移量轉換爲時區或日期

我從IPInfoDB的API獲取地理IP數據,它返回UTC 的時區偏移量,包括 DST(如果當前已反映)。

例如,我住在EST(-5),目前是DST,所以地理IP API返回(-04:00)作爲偏移量。

這真是太棒了,因爲DST是一個令人頭痛的頭痛。但令我驚訝的是,這引起了另一場頭痛。

我將這些數據加載到PHP中,通過AJAX傳遞給應用程序。我希望在應用程序中擁有本地IP地址的實時時間。

我已經完全設置好了,但我瘋了,試圖找出如何設置PHP時區匹配偏移量,所以我可以抓住當前小時date('H');和分鐘date('i');傳遞給通過AJAX。

我不確定是否有一個特定的函數可以根據該偏移量給出當前的小時數和分鐘數,或者是否存在一種基於偏移量設置時區的實際方法(如果使用了DST,將會應用if有效)。

我一直在搜索和搜索谷歌找到答案,但我做的更具體,因爲已經應用了DST。

我在PHP.net上發現了一個函數,它似乎可以做到這一點(它適用於我的時區並返回正確的時間),但對於其他時區(例如PST),即使偏移量爲1小時,正確(-07:00與DST)。

從函數返回的時區是Chile/EasterIsland,我有一種感覺是原因。如果可以的話,我會讓這隻適用於美國,但我確實需要它在全球範圍內。

這是我現在的功能。請原諒非常混亂的代碼。在過去的幾個小時裏,我一直在玩弄很多東西,試圖找出解決方案。

大部分功能都在網上找到。

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; 
} 

我包括用於某些時區是:30:45那裏的開關/箱。可能還有一種方法可以將這種情況也包括在內,而不需要開關/外殼。

注意:偏移總是從地理IP API返回+00:00-00:00

我會感謝任何幫助或在正確的方向點。我並不是PHP新手,但補償對我來說是一個新故事。謝謝!

回答

42

它可以很簡單地完成,通過旋轉偏移秒,並將它傳遞給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

timezone_name_from_abbr控制第三個參數是否調整爲夏令時或不。

Bug #44780

timezone_name_from_abbr()將在一段時間區 偏移返回false。特別是 - 夏威夷,它有一個從GMT偏移-10,-36000 秒。

參考文獻:

+0

這是我一直在尋找的東西。似乎它會工作得很好,但我需要花一些時間並測試它。無論哪種方式,您都是我所追求的最接近解決方案的人。我感謝您的幫助,並且您有幸獲得了賞金:) – 2012-08-11 23:02:54

+1

請注意,有一個錯誤:https://bugs.php.net/bug.php?id=44780 – 2012-10-07 10:16:28

+0

@HonzaJavorek有趣的是,我認爲這是因爲某些地點不適合夏令時。不過,我想我已經找到了解決方案。 – 2012-10-07 20:08:55

1

您可能想看看DateTime PHP擴展(在所有PHP版本中,默認情況下啓用&> = 5.2.0,除非它在編譯時被明確禁用)。

它可以滿足您在這裏所需的一切。

4
date_default_timezone_set('UTC'); 

$timezones = array(); 
foreach (DateTimeZone::listAbbreviations() as $key => $array) 
{ 
    $timezones = array_merge($timezones, $array); 
} 

$utc    = new DateTimeZone('UTC'); 
$timezone_offset = '+02:00'; # 2H 
$sign    = substr($timezone_offset, 0, 1) == '+'? '': '-'; 
$offset    = substr($timezone_offset, 1, 2) . 'H' . substr($timezone_offset, 4, 2) . 'M'; 

$operation = $sign == ''? 'add': 'sub'; 

$start = new DateTime('', $utc); 
$date = new DateTime('', $utc); 

$date->{$operation}(new DateInterval("PT{$offset}")); 

$offset = $start->diff($date)->format('%r') . ($start->diff($date)->h * 3600 + $start->diff($date)->m * 60 + $start->diff($date)->s); # 7200 (2H) 

echo $offset, PHP_EOL; 
echo $date->format('Y-m-d H:i:s'), PHP_EOL; 

foreach($timezones as $timezone) 
{ 
    if($timezone['offset'] == $offset) 
    { 
     echo $timezone['timezone_id'], PHP_EOL; 
    } 
} 

我可能在某些部分誤解了你,但我希望它有幫助,如果你可以更具體一些,我可能會更有幫助。

對於智利獲得:上面的示例中的

-25200 (-7h) 
2012-08-07 18:05:24 (current time 2012-08-08 01:05:24) 
Chile/EasterIsland 

輸出:

7200 
2012-08-08 02:49:56 
Europe/London 
Europe/Belfast 
Europe/Gibraltar 
Europe/Guernsey 
Europe/Isle_of_Man 
Europe/Jersey 
GB 
Africa/Khartoum 
Africa/Blantyre 
Africa/Bujumbura 
Africa/Gaborone 
Africa/Harare 
Africa/Kigali 
Africa/Lubumbashi 
Africa/Lusaka 
Africa/Maputo 
Africa/Windhoek 
Europe/Berlin 
Africa/Algiers 
Africa/Ceuta 
Africa/Tripoli 
Africa/Tunis 
Arctic/Longyearbyen 
Atlantic/Jan_Mayen 
CET 
Europe/Amsterdam 
Europe/Andorra 
Europe/Athens 
Europe/Belgrade 
Europe/Bratislava 
Europe/Brussels 
Europe/Budapest 
Europe/Chisinau 
Europe/Copenhagen 
Europe/Gibraltar 
Europe/Kaliningrad 
Europe/Kiev 
Europe/Lisbon 
Europe/Ljubljana 
Europe/Luxembourg 
Europe/Madrid 
Europe/Malta 
Europe/Minsk 
Europe/Monaco 
Europe/Oslo 
Europe/Paris 
Europe/Podgorica 
Europe/Prague 
Europe/Riga 
Europe/Rome 
Europe/San_Marino 
Europe/Sarajevo 
Europe/Simferopol 
Europe/Skopje 
Europe/Sofia 
Europe/Stockholm 
Europe/Tallinn 
Europe/Tirane 
Europe/Tiraspol 
Europe/Uzhgorod 
Europe/Vaduz 
Europe/Vatican 
Europe/Vienna 
Europe/Vilnius 
Europe/Warsaw 
Europe/Zagreb 
Europe/Zaporozhye 
Europe/Zurich 
WET 
Europe/Kaliningrad 
Europe/Helsinki 
Africa/Cairo 
Africa/Tripoli 
Asia/Amman 
Asia/Beirut 
Asia/Damascus 
Asia/Gaza 
Asia/Istanbul 
Asia/Nicosia 
EET 
Europe/Athens 
Europe/Bucharest 
Europe/Chisinau 
Europe/Istanbul 
Europe/Kaliningrad 
Europe/Kiev 
Europe/Mariehamn 
Europe/Minsk 
Europe/Moscow 
Europe/Nicosia 
Europe/Riga 
Europe/Simferopol 
Europe/Sofia 
Europe/Tallinn 
Europe/Tiraspol 
Europe/Uzhgorod 
Europe/Vilnius 
Europe/Warsaw 
Europe/Zaporozhye 
Asia/Jerusalem 
Asia/Gaza 
Asia/Tel_Aviv 
MET 
Africa/Johannesburg 
Africa/Maseru 
Africa/Mbabane 
Africa/Windhoek 
Africa/Windhoek 
Africa/Ndjamena 
Europe/Lisbon 
Europe/Madrid 
Europe/Monaco 
Europe/Paris 
WET 
Europe/Luxembourg 

哪個指甲我的時區。

+0

你有點偏離我所尋找的,但這是一個可行的解決方案,我只是相信這是一個過分的設計比它必須。我欣賞幫助。 – 2012-08-11 23:01:39

+0

@BigRoss不喝幾杯後就不會編碼:)))很高興你解決了這個問題。乾杯。 – 2012-08-11 23:06:12

1

它是輕率映射時區偏移回時區標識符。許多時區共享相同的偏移量。

即使在一個國家內,這也是有問題的。請考慮在美國,中央標準時間(America/Chicago)和東部夏令時(America/New_York)在一年的不同時間使用-5偏移量。

還要考慮到有些時候,這兩個時區的BOTH同時使用-5。例如,2013年11月3日星期日UTC-5上午1:00可能是CDT或EST。你無法區分只是-5它是哪個時區。

Details here.

0

您可以使用GMT時間也和它之後

<?php 
echo gmdate("M d Y H:i:s", mktime(0, 0, 0, 1, 1, 1998)); 
?> 

轉換成您的要求GMT是指格林威治時間這是常見的遍佈世界各地。

3
$UTC_offset = '+03:00'; 
$date  = new \DateTime('now', 'UTC'); 
var_dump($date); 
$timezone = new \DateTimeZone(str_replace(':', '', $UTC_offset)); 
$date->setTimezone($timezone); 
var_dump($date); 

結果:

class DateTime#205 (3) { 
    public $date => 
    string(26) "2015-01-20 06:00:00.000000" 
    public $timezone_type => 
    int(3) 
    public $timezone => 
    string(3) "UTC" 
} 
class DateTime#205 (3) { 
    public $date => 
    string(26) "2015-01-20 09:00:00.000000" 
    public $timezone_type => 
    int(1) 
    public $timezone => 
    string(6) "+03:00" 
} 
0

現今DateTimeZone構造函數可以明確接受UTC偏移,這是我理解你。

所以才:

$timeZone = new DateTimeZone('+0100'); 

文檔:

http://php.net/manual/en/datetimezone.construct.php

注:每個該文檔的鏈接,這個新的構造使用自PHP版本5.5.10可用。