
Три провинции Канады отменили перевод часов: почему legacy-приложения на VB6 и .NET теперь ошибаются на час
В ночь на 1 ноября 2026 года Британская Колумбия, Альберта и Манитоба не перевели часы назад и остались на постоянном времени: UTC−7, UTC−6 и UTC−5. Уже через сутки The Register описал, как администраторы Microsoft 365 переносят встречи вручную и временно ставят пользователям пояс Боготы. Если ваше приложение на VB6 или .NET хранит, показывает или планирует время для пользователей из Канады, оно, скорее всего, сейчас ошибается на час.
Проблема не канадская, а архитектурная. Любое государство может поменять правила перехода на летнее время, и каждый раз выясняется, где в старом коде зашиты смещения, собственные таблицы перехода или идентификаторы поясов в базе. Похожий урок многие команды уже проходили с Россией в 2011 и 2014 годах, с Турцией в 2016-м и с Мексикой в 2022-м.
Такую ревизию проще провести один раз и как следует, чем чинить после каждого указа. Если в команде не осталось человека, который помнит, где в учётной системе считается время, её можно отдать команде, которая сопровождает и переписывает корпоративные приложения, а самим сосредоточиться на проверке результата. Ниже о том, что именно сломалось и что проверить.
Что изменилось 1 ноября
По справке Microsoft, три провинции больше не переходят на зимнее время: Британская Колумбия остаётся на UTC−07:00, Альберта на UTC−06:00, Манитоба на UTC−05:00. К Альберте присоединились затронутые районы Северо-Западных территорий, к Манитобе — северо-запад Онтарио.
В Windows для них появились отдельные пояса: «(UTC-05:00) Manitoba», «(UTC-06:00) Mountain Time (Alberta)» с идентификатором Alberta Standard Time и «(UTC-07:00) Pacific Time (British Columbia)». Их принесло обновление 13 октября для всех поддерживаемых версий Windows, включая Windows Server. Устройства с автоматическим выбором пояса переключились сами, остальным пояс нужно выбрать вручную.
В самих продуктах Microsoft переход прошёл неровно. По данным The Register, встречи в Outlook после 1 ноября могут стоять на час не там. Командлет для разовых встреч в Exchange Online обещан на этой неделе, для повторяющихся срока нет. SharePoint Server получит правильные пояса 10 ноября, Power Pages — 12 декабря. Для Power Automate и Fabric Microsoft временно советует ставить «чужие» пояса: Саскачеван, Юкон или Боготу.
Почему старый код ошибается на час
У legacy-приложений три типичных места, где это ломается.
Идентификатор пояса в настройках или базе. Пользователю из Эдмонтона когда-то записали Mountain Standard Time. Этот пояс по-прежнему переходит на зимнее время, как в США, поэтому с 1 ноября TimeZoneInfo показывает ему время на час раньше. Обновление Windows этого не исправит: идентификатор в базе остался старым.
Смещения и правила в коде. В VB6 часто встречается собственная таблица «пояс — смещение — даты перехода» или прямое DateAdd("h", -7, ...). Такой код не узнает о новых правилах никогда.
Кэш и сервер. .NET Framework и .NET на Windows читают правила из реестра и держат их в кэше. Служба, запущенная до установки обновления, работает со старыми данными до перезапуска. Функция AT TIME ZONE в SQL Server тоже берёт правила из Windows, так что сервер БД без октябрьского обновления посчитает время по-старому. На Linux .NET пользуется системным пакетом tzdata: правила для Манитобы добавил выпуск IANA tzdata 2026e. В нём есть временный обходной приём для CLDR 48.2.2 и ICU 78.3, которые ещё не умеют описывать такую смену правил.
Как проверить приложение
Сначала убедитесь, что машина вообще знает новые пояса, затем сравните перевод времени по старому и новому поясу.
' Какие новые канадские пояса знает Windows на этой машине
For Each tz In TimeZoneInfo.GetSystemTimeZones()
If tz.DisplayName.Contains("Alberta") OrElse tz.DisplayName.Contains("British Columbia") _
OrElse tz.DisplayName.Contains("Manitoba") Then
Console.WriteLine($"{tz.Id} | {tz.DisplayName}")
End If
Next
' 1 декабря 2026, 17:00 UTC: старый пояс покажет 10:00, а в Эдмонтоне уже 11:00
Dim utc = New DateTime(2026, 12, 1, 17, 0, 0, DateTimeKind.Utc)
Dim oldTz = TimeZoneInfo.FindSystemTimeZoneById("Mountain Standard Time")
Dim newTz = TimeZoneInfo.FindSystemTimeZoneById("Alberta Standard Time")
Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(utc, oldTz))
Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(utc, newTz))
' Долгоживущая служба после установки обновления: сбросить кэш поясов
TimeZoneInfo.ClearCachedData()FindSystemTimeZoneById("Alberta Standard Time") бросает TimeZoneNotFoundException, на машине нет октябрьского обновления. Для службы этого достаточно, чтобы считать время по-старому, даже если в коде всё исправлено. Подробно метод описан в документации TimeZoneInfo.ClearCachedData.Дальше ищите по коду: FindSystemTimeZoneById, строки с названиями поясов, DateAdd("h", GetTimeZoneInformation в VB6 и любые таблицы с датами перехода. Как устроить такой поиск по всему проекту, мы разбирали в чек-листе аудита legacy-кодовой базы на VB.NET. Прежде чем править расчёт времени, зафиксируйте текущее поведение тестами: приёмы для старого кода собраны в статье о тестах для legacy VB.NET.
Что сделать на этой неделе
TimeZoneInfo.ClearCachedData(); 3) найти пользователей и объекты из Британской Колумбии, Альберты, Манитобы, Северо-Западных территорий и северо-запада Онтарио и перевести их на новые пояса; 4) пересчитать будущие события и расписания, сохранённые в местном времени; 5) убрать из кода жёстко заданные смещения.Если приложение ещё работает на .NET 8 или 9, совместите правку с переходом на .NET 10: их поддержка заканчивается 10 ноября 2026 года, об этом мы писали в новости о внеплановом патче .NET.
Вопросы и ответы
Если приложение хранит время в UTC, проблема меня касается?
Хранение в UTC защищает данные, но не отображение. Ошибка появится там, где UTC переводится в местное время: в отчётах, интерфейсе, письмах и расписаниях. Если для перевода используется старый пояс Mountain, Pacific или Central Standard Time, жители трёх провинций зимой увидят время на час раньше настоящего.
Хватит ли установить октябрьское обновление Windows?
Нет. Обновление добавляет новые пояса, но приложение продолжит пользоваться идентификатором, который записан у него в настройках или в базе. Пользователей и записи из трёх провинций нужно перевести на новые пояса, а долгоживущие службы перезапустить или вызвать TimeZoneInfo.ClearCachedData().
Что с приложениями на .NET под Linux?
На Linux .NET берёт правила из системного пакета tzdata. Идентификаторы America/Edmonton, America/Vancouver и America/Winnipeg не меняются, меняются правила внутри, поэтому достаточно обновить tzdata в образе или на сервере и перезапустить процесс. Отдельно проверьте преобразование IANA-идентификаторов в Windows-идентификаторы: оно зависит от данных ICU.