【问题标题】:Convert UTC offset to timezone or date将 UTC 偏移量转换为时区或日期
【发布时间】:2012-08-02 23:27:50
【问题描述】:

给你一个头疼的东西。

我正在从IPInfoDB 的 API 获取地理 IP 数据,它返回与 UTC包括 DST 的时区偏移量(如果当前反映)。

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

这太棒了,因为 DST 令人头疼。但令我惊讶的是,它引起了另一个头痛。

我在 PHP 中加载这些数据以通过 AJAX 传递给应用程序。我想在应用程序上获得 IP 地址的实时本地时间。

我已经完美地设置了这一切,但我疯狂地试图弄清楚如何设置 PHP 时区以匹配偏移量,这样我就可以获取当前小时 date('H'); 和分钟 date('i'); 以通过 AJAX 传递.

我不确定是否有一个特定的函数可以根据该偏移量给我当前的小时和分钟,或者是否有一种实用的方法可以根据偏移量设置时区(如果是,则已经应用了 DST有效)。

我一直在搜索和搜索 Google 以找到答案,但我正在做的更具体,因为 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 的开关/案例。可能有一种方法可以在不需要 switch/case 的情况下包含它。

注意: 偏移量总是从 geo IP API 返回 +00:00-00:00

我将不胜感激任何帮助或正确方向的观点。我对 PHP 不是很熟悉,但偏移量对我来说是一个新故事。谢谢!

【问题讨论】:

    标签: php geolocation timezone timezone-offset


    【解决方案1】:

    您可能想看看DateTime PHP 扩展(它默认启用并包含在所有 PHP 版本 >= 5.2.0 中,除非它在编译时被特别禁用)。

    它可以很好地完成您在这里需要的一切。

    【讨论】:

      【解决方案2】:
      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
      

      这确定了我的时区。

      【讨论】:

      • 您与我所寻找的有点不同,但这是一个可行的解决方案,我只是相信它的设计有点过度设计。不过,我感谢您的帮助。
      • @BigRoss 喝了几杯后不编码 :))) 很高兴你解决了这个问题。干杯。
      【解决方案3】:

      这可以很简单地完成,只需将偏移量转换为秒并将其传递给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 秒。

      参考资料:

      【讨论】:

      • 这是我一直在寻找的东西。似乎它会工作得很好,但我需要一些时间来测试它。无论哪种方式,您都是我正在寻找的最接近解决方案的人。感谢您的帮助,您已经获得了丰厚的回报 :)
      • @HonzaJavorek 有趣,我认为这是因为某些地方不适应夏令时。不过,我想我已经找到了解决方案。
      • 我试试你的代码Asia/Kolkata,它的偏移量是+05:30,但输出是Asia/Karachi,知道吗?
      • @DS9 这可能是因为您将$offset 设置为时区字符串。将$offset 设置为一个数字(+05:30 将变为5.5),或者使用strtotimeexample)。如果时区为负数,则将减号放在strtotime 之前,而不是在时区中(例如$offset = -strtotime('5:00', 0))。
      • 我不明白通过从偏移字符串中去除除数字之外的所有内容并乘以 36 来获得有用的秒数?如果偏移字符串为负数或包含分钟,如 OP 的示例偏移量,这会给我完全不正确的结果。不过,对于积极的整个小时来说,它似乎确实有效。
      【解决方案4】:

      不建议将时区偏移映射回时区标识符。许多时区共享相同的偏移量。

      即使在一个国家内,这也是有问题的。考虑到在美国,中部标准时间 (America/Chicago) 和东部夏令时间 (America/New_York) 在一年中的不同时间都使用 -5 偏移量。

      还要考虑到有时这两个时区同时使用 -5。例如,UTC-5 的 2013 年 11 月 3 日星期日凌晨 1:00 可能是 CDT 或 EST。您无法仅通过 -5 来区分是哪个时区。

      Details here.

      【讨论】:

        【解决方案5】:

        您也可以使用 GMT 时间,然后将其转换为您的要求

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

        GMT 指的是全球通用的格林威治标准时间。

        【讨论】:

          【解决方案6】:
          $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"
          }
          

          【讨论】:

            【解决方案7】:

            现在 DateTimeZone 构造函数可以显式接受 UTC 偏移量,我知道你有。

            就这样:

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

            文档:

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

            注意:根据该文档链接,这种新的构造函数用法自 PHP 版本 5.5.10 起可用。

            【讨论】:

              【解决方案8】:

              这对我有用:

              function Get_Offset_Date($Offset, $Unixtime = false, $Date_Format = 'Y.m.d,H:i')
              {
                  try
                  {
                      $date   = new DateTime("now",new DateTimeZone($Offset));
                      if($Unixtime)
                      {
                          $date->setTimestamp($Unixtime);
                      }
              
                      $Unixtime = $date->getTimestamp();
                      return $date->format($Date_Format);
                  }
              
                  catch(Exception $e)
                  {
                      return false;
                  }
              }
              

              要使用它,只需调用它来获取当前时间:

              echo Get_Offset_Date('+2:00');
              

              这用于获取偏移日期(输入为 unixtime)

              echo Get_Offset_Date('+2:00',1524783867);
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2015-02-26
                • 2014-02-04
                • 2018-02-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-03-22
                相关资源
                最近更新 更多