dprh34164 2016-09-22 16:47
浏览 177

如何在PHP中从时区名称转换为时区偏移量,反之亦然

I've got a database full of users that each have a timezone column stored as a string (e.g. America/New_York). (And I know that storing them as offsets in the database makes way more sense, but cannot be done for this project) I've got a form where the user is able to change their timezone. Here was my first approach :

I have this populating a drop down (value => display):

protected $timezones = [
    '+00:00' => '(UTC +00:00) Western European Time',
    '+01:00' => '(UTC +01:00) Central European Time',
    '+02:00' => '(UTC +02:00) Eastern European Time',
    '+03:00' => '(UTC +03:00) Further-Eastern European Time',
    '+04:00' => '(UTC +04:00) Gulf Standard Time',
    '+05:00' => '(UTC +05:00) Pakistan Standard Time',
    '+05:30' => '(UTC +05:30) Indian Standard Time',
    '+05:45' => '(UTC +05:45) Nepal Time',
    '+06:00' => '(UTC +06:00) British Indian Ocean Time',
    '+07:00' => '(UTC +07:00) Thailand Standard Time',
    '+08:00' => '(UTC +08:00) Australian Western Standard Time',
    '+09:00' => '(UTC +09:00) Japan Standard Time',
    '+10:00' => '(UTC +10:00) Australian Eastern Standard Time',
    '+10:30' => '(UTC +10:30) Australian Central Daylight Savings Time',
    '+11:00' => '(UTC +11:00) Australian Eastern Daylight Savings Time',
    '+12:00' => '(UTC +12:00) New Zealand Standard Time',
    '+13:00' => '(UTC +13:00) New Zealand Daylight Time',
    '-01:00' => '(UTC -01:00) Eastern Greenland Time',
    '-02:00' => '(UTC -02:00) Brasilia Summer Time',
    '-03:00' => '(UTC -03:00) Atlantic Daylight Time',
    '-04:00' => '(UTC -04:00) Atlantic Standard Time',
    '-05:00' => '(UTC -05:00) Central Daylight Time',
    '-06:00' => '(UTC -06:00) Central Standard Time',
    '-07:00' => '(UTC -07:00) Pacific Daylight Time',
    '-08:00' => '(UTC -08:00) Pacific Standard Time',
    '-09:00' => '(UTC -09:00) Alaska Standard Time',
    '-10:00' => '(UTC -10:00) Hawaii-Aleutian Standard Time',
    '-11:00' => '(UTC -11:00) Niue Time',
];

This function to convert timezone string to offset :

 /**
 * convert timezone string to offset
 *   e.g. "America/New_York" to "-04:00"
 *
 * @param string $timezone time zone represented as a string
 *
 * @return mixed string or null
 */
protected function convertStringToOffset($timezone)
{
    $time = new \DateTime('now', new DateTimeZone($timezone));
    if ($time) {
        return $time->format('P');
    }
}

And this function to convert an offset to a string :

/**
 * convert timezone offset to string
 *    e.g. "-04:00" to "America/New_York"
 *
 * @param string $offset time zone represented as a numerical offset
 *
 * @return string time zone represented as a string
 */
protected function convertOffsetToString($offset)
{
    // 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);
    }
    return $tz;
}
  1. So on page load, I would pull the timezone from the database and convert it to it's offset. For example America/New_York would be converted to -04:00 and the (UTC -04:00) Atlantic Standard Time would be selected.

  2. Now the user would select a different timezone and submit the form. I would then convert the offset into a string and then store that into the database.

I ran into a problem :

  • $this->convertOffsetToString('+03:00'); returns Europe/Helsinki
  • $this->convertOffsetToString('+04:00'); returns Europe/Moscow

but!

  • $this->convertStringToOffset('Europe/Moscow'); returns +03:00
  • $this->convertStringToOffset('Europe/Helsinki'); returns +03:00

So if a user came to the form and had the timezone Europe/Moscow in the database, we would convert the string to it's offset getting +03:00 and the '(UTC +03:00) Further-Eastern European Time' would be selected.

Problem : two different offsets (e.g. +03:00 and +04:00) will be converted into two different string time zones. Those two different string timezones would be converted into the same offset! (e.g. +03:00). Can anyone come up with a safe scale-able solution to tackle this problem?

  • 写回答

1条回答 默认 最新

  • douhuangjie4503 2016-09-23 12:21
    关注

    First of all,

    I know that storing them as offsets in the database makes way more sense

    No, it doesn't. Offsets change. Many timezones apply daylight saving for part of the year and the dates/times at which they change vary.

    You should store the users timezone as a string from this list and then use that to create an instance of \DateTimeZone. For example:-

    $tzString = 'Europe/London';
    $tz = new \DateTimeZone($tzString);
    

    You are then able to get the correct offset for any time you wish using something like:-

    $tz->getOffset(new \DateTime());
    

    Example. Note that the offset produced in the example will vary between 0 and 3600 depending on what date and time you are viewing it.

    As you are storing the users timezone you never have to face the futility of trying to convert an offset to the correct timezone.

    I would suggest that this is a "safe scaleable solution" to your problem.

    评论

报告相同问题?

悬赏问题

  • ¥15 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看