Ежедневно каждый из нас взаимодействует со временем: мы встаем по будильнику, работаем по расписанию, живем по календарю. Недооценить значимость времени в жизни человека невозможно. Казалось бы, учитывая этот факт, работа с датами и временем в области программирования должна быть элементарной. Но на практике все иначе. За время моей работы в сфере веб-программирования я не раз сталкивался со странностями, скрытыми в современных системах времяисчисления. Чтобы в разработанных вами системах правильно обрабатывались дата и время, прежде всего необходимо разобраться в том, какие проблемы с этим связаны и какие решения для них уже найдены.

Временные зоны

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

Все временные зоны выражаются как положительное или отрицательное смещение от UTC (всемирного координированного времени). Здесь мы сталкиваемся с первым нюансом. Время по UTC не переводится ни зимой, ни летом, поэтому в странах, где осуществляется переход на летнее время, смещение относительно UTC меняется в зависимости от времени года.

Проблемы с временными зонами в сети

Часто во время регистрации на сайтах или форумах в интернете нас просят выбрать свой часовой пояс, приводя в качестве вариантов все возможные смещения относительно UTC. Это пример некорректной работы. Правда, в некоторых случаях там есть возможность автоматического перевода времени на летнее и обратно, но на практике это только добавляет проблем, ведь правила перехода на летнее время и обратно диктуются законодательством страны. Простой пример: Василий — гражданин РФ. В 2009 году Василий зарегистрировался на кулинарном форуме. В качестве своего часового пояса Василий указал UTC+3 и установил автопереход на летнее время. Осенью 2011 года Василию пришлось менять свои настройки на форуме в связи с отменой перевода часов в его государстве.

Казалось бы, ничего страшного не случилось, ну поменяет Василий настройки и будет жить спокойно. Но с этой проблемой сталкивается не только пользователь. Часто часовые пояса в программах задаются смещением относительно UTC, и после очередного изменения в законодательстве программа может дать сбой. Представьте, что такая программа регулирует подачу лекарств пациентам в реанимации. А если вспомнить бардак с отменой и возобновлением перехода на летнее время в Украине, невольно радуешься, что в наших реанимациях еще нет такой автоматизации.

База данных часовых поясов

Для решения подобных проблем существует база данных zoneinfo (основанная Артуром Олсоном). В ней временные зоны указываются парами Область/Локация, например Europe/Kiev. Это значит, что в одном часовом поясе может одновременно находиться множество временных зон. База данных содержит информацию по часовым поясам и правилам перехода на летнее время для каждой временной зоны начиная с 1970 года. Эта база данных обновляется как минимум несколько раз в год и во многих случаях это происходит автоматически, что позволяет разработчику не переживать об актуальности данных. Эта база данных используется в PHP при работе с датами начиная с версии 5.1.0. В прочих языках может потребоваться установка модуля для нее.

Системы времяисчисления

Системы времяисчисления Прежде чем говорить про современные системы времяисчисления, обратимся к истории. В 1884 году время по GMT было введено для международного использования. GMT — Greenwich Mean Time — это среднее солнечное (астрономическое) время меридиана, проходящего через Гринвичскую королевскую обсерваторию около Лондона. GMT стало точкой отсчета времени в других часовых поясах. В 1955 году были изобретены цезиевые атомные часы, позволявшие использовать более стабильную и удобную систему времяисчисления. В 1961 году была официально запущена система UTC (хотя название не было принято до 1967): мгновение 1 января 1961 года в 00:00:01.422818 по международному атомному времени (TAI) было равно 1 января 1961 года в 00:00:00.000000 по UTC, а одна секунда UTC равнялась 1.000000015 секунды TAI. Каждые несколько месяцев вводились временные скачки, а в конце каждого года менялась частота таких скачков.

Официальное название UTC не имеет официальной расшифровки. Англичане предложили использовать название Coordinated Universal Time и аббревиатуру CUT, а французы предложили название Temps Universel Coordonné и аббревиатуру TUC. Текущее название было компромиссным вариантом и совпадает с принципом именования нотаций других разновидностей всемирного времени (UT0, UT1, UT2 и т.д.).

В 1967 году секунда в системе СИ была переопределена с учетом частоты цезиевых атомных часов. После этого длина секунды UTC стала равна секунде в TAI, а размер временного скачка был установлен в 1 секунду. Первый секундный скачок, названный секундой координации или високосной секундой, произошел 30 июня 1972 года. С тех пор високосные секунды добавлялись в среднем раз в 19 месяцев, 30 июня либо 31 декабря. На сегодняшний день было добавлено уже 24 високосных секунды, что с учетом временных скачков, сделанных до 1972 года, дает разницу между UTC и TAI в 34 секунды. 30 июня 2012 года эта разница достигнет 35 секунд.

скриншот часов с сайта time.gov. Часы показывают високосную секунду, добавленную 31 декабря 2008 года CST (Central Standard Time zone) Скриншот часов с сайта time.gov. Часы показывают високосную секунду, добавленную 31 декабря 2008 года CST (Central Standard Time zone)

Зачем вообще понадобились эти временные скачки? Все дело в том, что средняя длительность светового дня медленно растет в связи с замедлением вращения Земли. На нынешний момент длительность дня по системе СИ (основанная на средней длительности светового дня между 1750 и 1892 годами) и длительность среднего светового дня отличаются примерно на 0.002 секунды.

Помимо равномерного замедления скорости вращения Земли, на ее скорость влияют разнообразные события. Например, в связи с землетрясением в Индийском океане в 2004 году световой день укоротился на 2.68 микросекунд. По неизвестным причинам, в 1999 году скорость вращения Земли увеличилась, средний световой день стал короче на 1 мс, и после 2000 года потребовалось меньше високосных секунд.

В связи с непредсказуемостью частоты вращения Земли, предвидеть необходимость введения високосных секунд более чем за 6 месяцев до факта невозможно. Решение о введении високосной секунды принимается специальной организацией и анонсируется в январе на 30 июня и в июле на 31 декабря.

Почему это важно

Что же все это значит с точки зрения разработчика? Это значит, что теоретически в минуте может быть не только 60, но и 61 и 59 секунд. Это значит, что единственная фиксированная величина — секунда (как минимум до тех пор, пока ее не переопределят в системе СИ). Это значит, что не имея на руках таблицы високосных секунд, невозможно преобразовать unix-время в дату и обратно или с точностью до секунды вычислить временной интервал между двумя датами. Нельзя с точностью до секунды выразить дату, находящуюся в будущем более чем на 6 месяцев.

В сфере IT существует еще одна распространенная современная система времяисчисления: unix-время. Эта система считает количество секунд, прошедших с 1 января 1970 года по UTC. В теории эта система позволила бы разработчикам не переживать по поводу неучтенных високосных секунд, однако на практике она их не учитывает. Кроме того, на данный момент unix-время хранится в формате 32-битного signed integer, что ограничивает даты, которые можно выразить в этой системе, промежутком от 13 декабря 1901 года до 19 января 2038 года. В некоторых современных ОС размер данных увеличен до 64 бит, что позволяет хранить временной интервал в более чем 20 раз превышающий возраст вселенной, но проблему с високосными секундами это не решает :)

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