Разработчику. Сборник рецептов PHP
Задавайте вопросы

Учет часовых поясов при определении времени

Вернуться назад

Задача

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

Решение

В случае простых вычислений можно непосредственно добавить или вычесть разность между двумя часовыми поясами:

// если локальное время – это зона EST
$time_parts = localtime();
// Калифорния (PST) на три часа раньше
$california_time_parts = localtime(time() - 3 * 3600);

В UNIX-системах, если не известно смещение между временными зонами, достаточно установить значение переменной окружения равным целевому часовому поясу:

putenv('TZ=PST8PDT');
$california_time_parts = localtime();

Обсуждение

Перед тем как начать обходить все углы и закоулки часовых поясов, мы хотим передать заявление, которое военно-морская обсерватория США представляет , а именно, что официальная информация о часовых поясах мира в некотором роде непостоянна, «поскольку нации суверенны и могут изменять и изменяют свои системы хранения времени так, как они считают нужным». Поэтому, помня о превратностях международных отношений, которые властвуют над нами, мы прибегаем к нескольким способам, позволяющим правильно работать с часовыми поясами.

Для относительно простой обработки смещений между часовыми поясами в программе организуется массив, в котором хранятся различные смещения относительно пояса UTC. Определив часовой пояс пользователя, просто прибавьте это смещение к соответствующему UTC времени, и функции, которые выводят на печать UTC время (т. е. gmdate(), gmstrftime()), смогут напечатать соответствующим образом скорректированное время.

// Определяем текущее время
$now = time();

// Калифорния на 8 часов отстает от зоны UTC
$now += $pc_timezones['PST'];

// Используйте функции gmdate() или gmstrftime()
// для вывода Калифорнийского времени
print gmstrftime('%c', $now);

В вышеприведенном коде смещения относительно зоны UTC хранятся в массиве $pc_timezones:

// Из Perl Time::Timezone
$pc_timezones = array(
'GMT' => 0,         // Среднее время по гринвичскому меридиану
'UTC' => 0,         // Универсальное (Скоординированное) время
'WET' => 0,         // Западноевропейское время
'WAT' => -1*3600,         // Западноафриканское время
'AT' => -2*3600,         // Время Азорских островов
'NFT' => -3*3600 1800,         // Время Ньюфаундленда
'AST' => -4*3600,         // Стандартное Атлантическое время
'EST' => -5*3600,         // Восточное стандартное время
'CST' => -6*3600,         // Центральное стандартное время
'MST' => -7*3600,         // Стандартное Горное время
'PST' => -8*3600,         // Стандартное Тихоокеанское время
'YST' => -9*3600,         // Стандартное Юконское время
'HST' => -10*3600,         // Стандартное Гавайское время
'CAT' => -10*3600,         // Время Центральной Аляски
'AHST' => -10*3600,         // Стандартное время Аляска-Гавайи
'NT' => -11*3600,         // Время города Ном
'IDLW' => -12*3600,         // К западу от международной линии смены дат
'CET' => +1*3600,         // Центральноевропейское время
'MET' => +1*3600,         // Среднеевропейское время
'MEWT' => +1*3600,         // Зимнее Среднеевропейское время
'SWT' => +1*3600,         // Зимнее Шведское время
'FWT' => +1*3600,         // Зимнее Французское время
'EET' => +2*3600,         // Восточноевропейское время, СССР Зона 1
'BT' => +3*3600,         // Багдадское время, СССР Зона 2
'IT' => +3*3600+1800,         // Иранское время
'ZP4' => +4*3600,         // СССР Зона 3
'ZP5' => +5*3600,         // СССР Зона 4
'IST' => +5*3600+1800,         // Стандартное Индийское время
'ZP6' => +6*3600,         // СССР Зона 5
'SST' => +7*3600,         // Южносуматранское время, СССР Зона 6
'WAST' => +7*3600,         // Стандартное Западноавстралийское время
'JT' => +7*3600+1800,         // Время острова Ява
'CCT' => +8*3600,         // Время Китайского побережья, СССР Зона 7
'JST' => +9*3600,         // Стандартное Японское, СССР Зона 8
'CAST' => +9*3600+1800,         // Стандартное Центральноавстралийское время
'EAST' => +10*3600,         // Стандартное Восточноавстралийское время
'GST' => +10*3600,         // Стандартное Гуамское время, СССР Зона 9
'NZT' => +12*3600,         // Новозеландское время
'NZST' => +12*3600,         // Стандартное Новозеландское время
'IDLE' => +12*3600         // К востоку от международной линии смены дат
);

В UNIX-системах преобразования можно выполнять при помощи библиотеки zoneinfo. Это позволяет сделать код более компактным, а обработку DST более прозрачной, как показано в рецепте (Учет перехода на летнее время).

Чтобы получить преимущества библиотеки zoneinfo в PHP, все вычисления с международными датами надо выполнять на основе меток времени UNIX. Последние следует генерировать из частей времени с помощью функции pc_mktime(), показанной в примере описанном ниже:

function pc_mktime($tz,$hr,$min,$sec,$mon,$day,$yr) {
        putenv("TZ=$tz");
        $a = mktime($hr,$min,$sec,$mon,$day,$yr);
        putenv('TZ=EST5EDT'); // замена пояса EST5EDT на часовой пояс
                                                                вашего сервера!

return $a;
}

Вызов функции putenv() до вызова mktime() позволяет обмануть системную функцию mktime(), заставляя ее думать, что она находится в другом часовом поясе. После вызова функции mktime() необходимо восстановить истинный часовой пояс. На восточном побережье Соединеных Штатов это пояс EST5EDT. Замените его соответствующим значением географического положения вашего компьютера (см. далее).

Части времени преобразуются в метки времени с помощью функции pc_mktime(). Ее двойником, который превращает метки времени в форматированную строку времени и части времени, является функция pc_strftime(), показанная в примере, описанном ниже:

function pc_strftime($tz,$format,$timestamp) {
        putenv("TZ=$tz");
        $a = strftime($format,$timestamp);
        putenv('TZ=EST5EDT'); // замена пояса EST5EDT на часовой пояс
                                                                вашего сервера!
        return $a;
}

В этом примере применен тот же способ обмана системной функции, к которому прибегает функция pc_mktime() для получения правильного результата от функции strftime().

Существенным в этих функциях является то, что не надо беспокоиться о смещениях различных временных поясов относительно UTC независимо от того, действует ли переход на летнее время или какие-либо другие особенности часовых поясов. Достаточно установить соответствующий часовой пояс, а системная библиотека сделает все остальное.

Учтите, что значение переменной $tz в обеих функциях должно быть именем не часового пояса, а зоны zoneinfo. Пояса zoneinfo имеют больше особенностей, чем часовые пояса, так как они соответствуют определенным местам. В Таблице показанной ниже, соответствия между определенными поясами zoneinfo и смещениями относительно зоны UTC. Последняя колонка показывает, происходит ли в данном поясе переход на летнее время.

UTC смещение (часы) UTC смещение (секунды) зона zoneinfo DST?
−12 −43200 Etc/GMT+12 Нет
−11 −39600 Тихий океан/Остров Мидуэй Нет
−10 −36000 США/Алеутские острова Да
−10 −36000 Тихий океан/Гонолулу Нет
−9 −32400 Америка/Анкоридж Да
−9 −32400 Etc/GMT+9 Нет
−8 −28800 PST8PDT Да
−8 −28800 Америка/Даусон-Крик Нет
−7 −25200 MST7MDT Да
−7 −25200 MST Нет
−6 −21600 CST6CDT Да
−6 −21600 Канада/Река Саскачеван Нет
−5 −18000 EST5EDT Да
−5 −18000 EST Нет
−4 −14400 Америка/Галифакс Да
−4 −14400 Америка/Пуэрто-Рико Нет
−3,5 −12600 Америка/Сэйнт-Джонс Да
−3 −10800 Америка/Буэнос-Айрес Нет
0 0 Европа/Лондон Да
0 0 GMT Нет
1 3600 CET Да
1 3600 GMT-1 Нет
2 7200 EET Нет
2 7200 GMT-2 Нет
3 10800 Азия/Багдад Да
3 10800 GMT-3 Нет
3,5 12600 Азия/Тегеран Да
4 14400 Азия/Дубай Нет
4 14400 Азия/Баку Да
4,5 16200 Азия/Кабул Нет
5 18000 Азия/Ташкент Нет
5,5 19800 Азия/Калькутта Нет
5,75 20700 Азия/Катманду Нет
6 21600 Азия/Новосибирск Да
6 21600 Etc/GMT-6 Нет
6,5 23400 Азия/Рангун Нет
7 25200 Азия/Джакарта Нет
8 28800 Гонконг Нет
9 32400 Япония Нет
9,5 34200 Австралия/Дарвин Нет
10 36000 Австралия/Сидней Да
10 36000 Тихий океан/Остров Гуам Нет
12 43200 Etc/GMT-13 Нет
12 43200 Тихий океан/Окленд Да

В разных странах переход на летнее и зимнее время не происходит в один и тот же день. Чтобы вычислить время с учетом перехода на летнее время в соответствии с географическим положением пункта, выберите пояс zoneinfo, который ближе всего находится к данному пункту.

Вернуться назад

Рейтинг@Mail.ru

Яндекс.Метрика

Индекс цитирования


Рейтинг Сайтов ДОСКИ.РУ