Тестування проблеми 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 # Стартувати раніше й іти вшестеро швидше — межа настане приблизно за 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-розрядна межа з того боку недосяжна, а сесія попереджає, коли бачить, що програма відкриває мережеве з’єднання.