Pull to refresh

Сказка о потерянном московском времени, или в чем ошиблись ребята из Microsoft

Programming.NET


На днях я с сожалением обнаружил, что в семействе операционных систем Windows содержится неверная информация о московском времени. Примечательно, что ошибка до сих пор не исправлена, хотя просочилась в систему еще в 2011-ом году. Последствия ошибки будут показаны с помощью .NET, но это актуально для всех технологий, которые доверяют данным Windows о часовых поясах и смещениях времени.

Итак, на моей машине выставлен часовой пояс Москвы:


Создаем дату 09.03.2014 10:00 в UTC. Затем переводим ее в московское время. Как вы знаете, сейчас для Москвы круглый год действует смещение UTC+04:00:

var dt = new DateTime(2014, 3, 9, 10, 0, 0, DateTimeKind.Utc); //09.03.2014 10:00 UTC
Console.WriteLine("UTC: {0},\nMoscow: {1}",dt, dt.ToLocalTime() );

Получаем:
UTC: 09.03.2014 10:00:00,
Moscow: 09.03.2014 14:00:00

Что же, неплохо. Однако, окунемся чуть глубже в прошлое. До отмены перевода часов на зимнее время наша страна переводила стрелки часов два раза в год. Зимой московское время имело смещение UTC+03:00, а летом UTC+04:00.
Повторим операцию для даты 09.03.2010 10:00 в UTC.

var dt = new DateTime(2010, 3, 9, 10, 0, 0, DateTimeKind.Utc); //09.03.2010 10:00 UTC
Console.WriteLine("UTC: {0},\nMoscow: {1}",dt, dt.ToLocalTime() );

Получим:
UTC: 09.03.2010 10:00:00,
Moscow: 09.03.2010 14:00:00

Что-то не так. В марте 2010-го действовало зимнее время, для которого смещение должно быть +03:00 часа, а не +04:00. Аналогично, для летнего времени(июнь 2010-го):

var dt = new DateTime(2010, 6, 9, 10, 0, 0, DateTimeKind.Utc);  //09.06.2010 10:00 UTC
Console.WriteLine("UTC: {0},\nMoscow: {1}",dt, dt.ToLocalTime() );

UTC: 09.06.2010 10:00:00,
Moscow: 09.06.2010 15:00:00

Летом смещение было +04:00, но мы получили смещение +05:00, которого в московском часовом поясе никогда не было!

В чем же дело?


Информация о часовых поясах хранится в реестре Windows и обновляется с помощью патчей. Вместо того, чтобы использовать базу данных tz database, в которой актуализируется информация о всемирных часовых поясах (и которая используется в *nix системах, BSD системах и Mac OS X), в Microsoft поддерживают собственную базу.

Если немного упростить, информация в ней хранится так. Для часового пояса указывается базовое смещение относительно UTC, затем идет информация о переходах на летнее/зимнее время. В ней говорится о том когда выполняется переход и как изменяется базовое смещение. До отмены перехода на зимнее время для Москвы действовали такие цифры:
  • Базовое смещение: UTC+03:00
  • Зимнее время = базовое смещение
  • Летнее время = базовое смещение +01:00

После отмены перехода на зимнее время, вышел hotfix, который должен был зафиксировать для Москвы круглогодичное смещение UTC+04:00. Но тут произошла ошибка. Вместо того, чтобы задать перманентное летнее время, в Microsoft поменяли базовое смещение с +03:00 на +04:00 и включили перманентное зимнее время. Но дело в том, что база часовых поясов Microsoft не содержит исторической информации об изменении базового смещения. В итоге для дат до лета 2010 получается следующая ситуация. Когда действует зимнее время, его смещение совпадает с базовым. До выхода патча это было +03:00, а теперь стало +04:00. Когда действует летнее время, оно имеет смещение +01:00 относительно базового. Раньше получалось +04:00, а после выхода патча стало +05:00.

Через какое-то время данная проблема была замечена, и в Microsoft подготовили очередной hotfix, который должен был исправить сложившуюся ситуацию. Однако его установка не исправляет проблему.

На текущий момент экспериментально выяснено, что ошибка воспроизводится на Windows 7, Windows 8, а так же Windows Server 2008 R2. В случае .NET данную проблему можно решить, работая с датами с помощью библиотеки NodaTime, которая использует базу данных tz database.
Tags:.NETmicrosoftwindowstimezonestz databaseперевод часоввремя
Hubs: Programming .NET
Total votes 110: ↑97 and ↓13+84
Views54K
Comments Comments 26

Popular right now

Top of the last 24 hours