【问题标题】:Get timezone location from offset从偏移量获取时区位置
【发布时间】:2014-03-27 15:25:57
【问题描述】:

我需要在php 中设置timezone。我有新的timezone 的偏移量(以秒为单位),我想使用date_default_timezone_set 设置它。我没有将秒数转换为 +0200 之类的偏移量的问题,但我认为这还不够。
根据我阅读手册后的理解,我需要提供类似America\New_York 的参数。有没有办法将偏移量转换为特定位置?

【问题讨论】:

    标签: php timezone environment-variables


    【解决方案1】:

    有,是的。我将通过 uınbɐɥs 引用 excellent answer

    $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);
    

    我建议您查看his answer in detail,因为您可能需要考虑一个错误。

    【讨论】:

      【解决方案2】:

      您可以在DateTimeZone::listAbbreviations() 的输出中搜索与您的偏移量匹配的时区:

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

      例如:

      convertTimezoneOffsetToId(5040);
      

      返回:

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

      编辑

      作为Matt points out,这个函数没有考虑夏令时的历史变化。

      所以这是一个改进的功能:

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

      它使用DateTimeZone::getTransitions() 来获取每个时区在历史特定时刻的偏移量。

      例如:

      convertTimezoneOffsetToId(19800, time());
      

      返回当前偏移量为 19800 秒的时区列表:

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

      和:

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

      返回在 2000 年 1 月 1 日偏移 19800 秒的时区列表:

      Array
      (
          [0] => Asia/Kolkata
      )
      

      请注意Asia/Colombo 已消失,因为在 2000 年 1 月 1 日,它的偏移量为 21600 秒。

      【讨论】:

        【解决方案3】:

        已编辑(原答案完全废弃)

        一种方法是创建一个偏移小时字符串(例如+0800)到 PHP 识别的时区名称的映射。它不是一对一的,因为每个偏移量都有多个名称。但是由于您关心的是偏移量而不是名称,因此您的映射可以为任何给定的偏移量选择任何可用的时区名称。

        由于没有成百上千的时区,因此您最终只会得到大约 35 个条目的数组(在 30 分钟甚至 45 分钟标记处有几个时区)。

        这是一个几乎可以满足您需要的代码示例:

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

        for 循环只是演示了几乎基于迭代偏移量的所有不同时区的设置(为简单起见,我排除了 30 分钟和 45 分钟标记时区)。 p>

        以下是运行上述代码的输出摘录:

        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
        

        感谢@Eugene Manuilov his StackOverflow answer 关于 PHP 时区,因为这意味着我不必完全从头开始编写数组。

        【讨论】:

          【解决方案4】:

          您实际上无法可靠地做到这一点。每个命名时区都有多个随时间变化的独特属性。当前偏移量只是信息的部分。您还必须考虑夏令时的应用方式,每个时区的应用方式可能不同(或根本不适用)。您还必须考虑单个时区如何具有不同值的历史,因为它们可能已多次更改其偏移量或 DST 规则。

          您还应该考虑到,在任何给定时间,多个不同时区将使用相同的偏移量。如果你只是随便挑一个,你会忽略很多重要的细节!

          请阅读the timezone tag wiki

          这里的其他答案可能会返回一个值,但它们都是基于错误的假设。时区不能仅用一个数字来表示。

          【讨论】:

          • 非常好,感谢您提供指向 wiki 文章的链接。我现在改进了我的解决方案,以考虑 DST 的历史变化。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-07
          • 2013-10-15
          • 1970-01-01
          • 2018-03-01
          • 1970-01-01
          • 2020-04-20
          相关资源
          最近更新 更多