Pull to refresh

Перевод часов в России 26 октября и icu4c

Reading time 2 min
Views 18K
Многие уже поставили обновление tzdata на свои любимые дистрибутивы и с нетерпением ждут конца света 26 октября 2014 года, когда можно будет в 2 часа ночи ощутить себя на все 3.

Но всё ли в порядке, если вы пишите на PHP и используете Symfony Forms, или просто php-intl?!
Мой ответ — нет.

Проверить себя вы можете просто:

<?php

$dateIn = '27.10.2014';
$tz = 'Europe/Moscow';
date_default_timezone_set($tz);
$intlDateFormatter = new \IntlDateFormatter('ru_RU', 2, -1, $tz, 1, 'dd.MM.yyyy');
$timestamp = $intlDateFormatter->parse($dateIn);

var_dump($intlDateFormatter->format($timestamp));
var_dump(date('d.m.Y', $timestamp));

На Debian Wheezy, CentOS 6 после обновления tzdata этот код выведет:
string(16) "27.10.2014"
string(16) "26.10.2014"

Это происходит потому, что у библиотеки icu своя база данных временных зон (это популярное занятие, например, также делает php, java, chrome и вагон другого софта) и при пересчете даты в unix timestamp она отнимает 4 часа, а php функция date прибавляет только 3, и если поле у вас только для ввода даты, то после отбрасывания времени вы получите на день меньше от введенной даты.

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

Все действия далее выполняются на отдельной VM под управлением CentOS 6.5 + remi + libicu-last.

По версии пакета определяем версию icu — 50.1.2 (исходники на сайте icu), также отсюда скачиваем все *.res файлы, где (в ссылке)
2014i — версия tzdata;
44 — версия libicu (44 и все что больше);
le — little endian (дада если у вас mips arm и т.п. вам может потребоваться другой набор ресурсов).

tar -zxf icu4c-50_1_2-src.tgz
cd icu/source
mkdir bin
./configure

# собираем утилиту icupkg
cd tools/icupkg/
make
cd ../../

# теперь обновим файл данных 
cd data/in/

# копируем скаченные ранее файлы в эту папку и добавляем их в файл данных
../../bin/icupkg -a zoneinfo64.res icudt50l.dat
../../bin/icupkg -a metaZones.res icudt50l.dat
../../bin/icupkg -a timezoneTypes.res icudt50l.dat
../../bin/icupkg -a windowsZones.res icudt50l.dat

# после чего запускаем сборку
cd ../../
make

В принципе, на этом можно остановиться и после сборки заменить в системе библиотеку из /usr/lib64/ на новую версию ./lib/libicudata.so.50.1.2.

Я же собрал пакет:

yum install rpmrebuild
rpmrebuild -e -p --notest-install libicu-last-50.1.2-10.el6.remi.x86_64.rpm

По строчке вида
BuildRoot: /root/.tmp/rpmrebuild.42406/work/root
определяем, где находятся распакованные файлы пакета и заменяем libicudata на свежую версию, получившийся пакет (rpmbuild напишет полный путь после сборки) можно установить с указанием ключа --force, так как мы не меняли его версию.

Спасибо за внимание!

UPD: Пользователи Ubuntu (Trusty|Precise) могут взять готовые пакеты в ppa хабрапользователя ranzhe
Tags:
Hubs:
+12
Comments 23
Comments Comments 23

Articles