datetime — Как преобразовать имя часового пояса в смещение часового пояса и наоборот в переполнении стека

У меня есть база данных, полная пользователей, каждый из которых имеет timezone столбец хранится в виде строки (например, America/New_York). (И я знаю, что хранить их как смещения в базе данных имеет гораздо больше смысла, но это невозможно сделать для этого проекта) У меня есть форма, в которой пользователь может изменить свой часовой пояс. Вот мой первый подход:

У меня есть это заполнение выпадающего списка (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',
];

Эта функция для преобразования строки часового пояса в смещение:

 /**
* 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');
}
}

И эта функция для преобразования смещения в строку:

/**
* 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. Поэтому при загрузке страницы я бы извлекал часовой пояс из базы данных и преобразовывал его в его смещение. Например America/New_York будет преобразован в -04:00 и (UTC -04:00) Atlantic Standard Time будет выбран.

  2. Теперь пользователь выберет другой часовой пояс и отправит форму. Затем я бы преобразовал смещение в строку и затем сохранил это в базе данных.

Я столкнулся с проблемой:

  • $this->convertOffsetToString('+03:00'); возвращается Europe/Helsinki
  • $this->convertOffsetToString('+04:00'); возвращается Europe/Moscow

но!

  • $this->convertStringToOffset('Europe/Moscow'); возвращается +03:00
  • $this->convertStringToOffset('Europe/Helsinki'); возвращается +03:00

Так что, если пользователь пришел в форму и имел часовой пояс Europe/Moscow в базе данных, мы конвертируем строку в ее смещение, получая +03:00 и '(UTC +03:00) Further-Eastern European Time' будет выбран.

проблема : два разных смещения (например, +03:00 а также +04:00) будет преобразован в два разных часовых пояса. Эти два разных часовых пояса будут преобразованы в одно и то же смещение! (например. +03:00). Может кто-нибудь придумать безопасное масштабируемое решение для решения этой проблемы?

1

Решение

Прежде всего,

Я знаю, что хранить их как смещения в базе данных имеет больше смысла

Нет, это не так. Смещения меняются. Многие часовые пояса применяют переход на летнее время для части года, и даты / время, в которые они меняются, варьируются.

Вы должны хранить часовой пояс пользователя в виде строки из этот список а затем использовать это для создания экземпляра \ DateTimeZone. Например:-

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

Затем вы можете получить правильный смещение на любое время, которое вы хотите, используя что-то вроде: —

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

пример. Обратите внимание, что смещение, полученное в примере, будет варьироваться от 0 до 3600, в зависимости от того, какую дату и время вы просматриваете.

Поскольку вы сохраняете часовой пояс пользователей, вам никогда не придется сталкиваться с тщетностью попыток преобразовать смещение в правильный часовой пояс.

Я бы предположил, что это «безопасное масштабируемое решение» вашей проблемы.

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]