Тестирование проблемы 2038 года в Windows
19 января 2038 года в 03:14:07 UTC заканчивается 32-разрядный знаковый счётчик секунд с 1970 года. Часы Windows идут дальше — а программы, форматы файлов и библиотеки, сохранившие собственную 32-разрядную копию, нет. Вот как найти их в одном приложении, не выставляя на всей машине дату 2038 года.
Где ошибка всё ещё может прятаться
Часы Windows — это 64-разрядный счётчик шагов по 100 наносекунд с 1601 года, так что 2038 года в
них нет. Тип time_t в библиотеке времени выполнения C по умолчанию 64-разрядный начиная
с Visual Studio 2005. Остаётся код, который выбрал 32-разрядное время или потерял старшую половину
по дороге:
- 32-разрядные сборки, скомпилированные с
_USE_32BIT_TIME_T, или напрямую вызывающие_time32и__time32_t. Документация Microsoft предупреждает, что такое приложение может отказать после 18 января 2038 года. - 64-разрядное время, сохранённое в
intилиlong. В Windows оба 32-разрядные, даже в 64-разрядной программе. - Библиотеки, форматы файлов, сетевые протоколы и столбцы баз данных, хранящие секунды с 1970 года в 32 битах.
- Код, прибавляющий годы. Двадцатилетняя лицензия, тридцатилетний график кредита или сертификат, действующий до 2040 года, пересекают границу задолго до наступления 2038 года.
Пересечение границы в одном приложении
Граница задана в UTC, а --at читается в поясе сессии, поэтому при ручной записи
момента зафиксируйте пояс как UTC. Готовый сценарий уже несёт абсолютный момент в UTC и в поясе не
нуждается.
# Сценарий: сессия стартует прямо на границе, 03:14:07 UTC chrono run "C:\apps\test.exe" --preset year-2038 --report session.txt # Стартовать за семнадцать секунд до неё и дать приложению пересечь границу на ходу chrono run "C:\apps\test.exe" --at 2038-01-19T03:13:50 --zone +00:00 --report session.txt # Стартовать раньше и идти в 60 раз быстрее — граница наступит примерно через 14 секунд chrono run "C:\apps\test.exe" --at 2038-01-19T03:00:00 --zone +00:00 --mode x60 # Или идти с сегодняшней датой и перепрыгнуть границу, пока приложение открыто chrono run "C:\apps\test.exe" --jump-after 5:2038-01-19T03:14:05 --zone +00:00
Через секунду после границы 32-разрядный знаковый счётчик становится равным -2 147 483 648, что читается как 13 декабря 1901 года, 20:45:52 UTC. Дата 1901 года, отрицательное число, элемент, который вдруг просрочился или оказался первым в сортировке, исключение из кода работы с датами — так переполнение выглядит со стороны.
Выставить настоящие часы на 2038 год — другой путь сюда, и он ломает больше всего. Что происходит, когда вы меняете системную дату →
Проверка того, что граница дошла до программы
Тест, который проходит лишь потому, что программа так и не увидела 2038 год, хуже, чем отсутствие теста. Отчёт в конце каждой сессии говорит, какие часы программа читала и сколько раз:
Chrono Mock - session report
target: .\build\test.exe
verdict: WORKS - time substitution took effect (processes: 1)
every process that read time saw the session clock
session: fake clock reached 2038-01-19T03:14:08
real elapsed 1.3s, fake elapsed 1.3s
covered channels (substituted, with call counts, 10 calls in all):
- pid 21240: GetSystemTimeAsFileTime (5 calls)
- pid 21240: GetLocalTime (5 calls)
Строка session подтверждает, что часы приложения перешли границу. Вердикт works со счётчиками вызовов означает, что программа читала часы и получила дату 2038 года; works partially называет каналы, которые она читала другим путём. Что означают вердикты →
Чего пройденный тест не доказывает
- Сдвинулись только собственные часы программы. Дата, которую она загружает из
файла или базы данных, — та, что там записана, так что дата 2038 года внутри данных — это
отдельный тест. Калькулятор дат вычислит такие даты за вас:
chrono calc --base 2038-01-19T03:14:07 --zone +00:00 --shift +1s - Сервер, с которым общается программа, по-прежнему живёт в настоящей дате. 32-разрядный предел с той стороны недосягаем, а сессия предупреждает, когда видит, что программа открывает сетевое соединение.