Як ми можемо розробити практики кодування, призначені для захисту від помилок високосного року? [зачинено]


80

Microsoft щойно оголосила, що помилка програмного забезпечення при підрахунку дат (за високосний рік) спричинила серйозні збої в роботі Windows Azure минулого тижня.

Чи справді це була проста помилка в судженнях, яка працювала DateTime.Now.AddYears(1)у високосний рік?

Які практики кодування могли б запобігти цьому?

EDIT Як зазначив dcstraw DateTime.Now.AddYears(1)у високосний рік, насправді повертає правильну дату в .NET. Отже, це не помилка фреймворку, а, очевидно, помилка в розрахунках дати.


7
Оскільки відповіді "один" немає, і це більше питання для обговорення, я думаю, що його слід перенести на програмістів. Також: Завжди Unit тестуйте свій код. Завжди.
Джордж Стокер

2
Проблеми з DateTime - це груба та складна проблема. Можна сказати щось на зразок робити все в UTC, але завжди буде якась точка перекладу ... і коли це трапляється, кількість перестановок, на які вам доведеться враховувати, щоб уникнути всіх помилок, приголомшує. Більшість з нас ніколи не працюватиме над системою, яка повинна піклуватися.
Джош

9
Я вважаю, що це питання занадто цікаве, щоб його можна було закрити :-)
Стівен

11
ЧОТИРИ голоси для закриття? Я сподіваюся, що ця погана вдача, що викликає щасливе близьке життя - це лише фаза еволюції СО. Це все просто трохи святіше, ніж ти.
Iain Holder

7
@IainMH повинен погодитися з вами, тим більше, що майже всі, хто закрив його, ніколи не задавали запитань більше, ніж кількома голосами.
Ллойд,

Відповіді:


95

Безсоромна вилка:

Використовуйте кращий API дати та часу

Вбудовані бібліотеки дат та часу .NET жахливо важко використовувати належним чином. Вони ж дозволяють робити все , що вам потрібно, але ви не можете висловити себе ясно через систему типу. DateTimeце безлад , DateTimeOffsetможе заснути вас думкою, що ви насправді зберігаєте інформацію про часовий пояс, коли цього немає, і TimeZoneInfoне змушує вас думати про все, що вам слід було б врахувати.

Жодне з них не забезпечує приємного вислову "просто час доби" або "просто дату", а також не чітко розмежовує "місцевий час" і "час у певному часовому поясі". А якщо ви хочете використовувати календар, відмінний від григоріанського, вам потрібно Calendarвесь час проходити клас.

Це все, чому я будую Noda Time - альтернативну бібліотеку дат та часу, побудовану на порту "движка" Joda Time, але з новим (і більш струнким) API зверху.

Деякі моменти, над якими ви можете задуматись, і які легко пропустити, якщо ви їх не знаєте:

  • Зіставити місцеву дату / час з часом у певному часовому поясі не так просто, як ви можете подумати. Конкретна локальна дата / час може траплятися один, два рази (неясність) або нульовий раз (вона пропускається) через перехід на літній час
  • Часові пояси в історичному плані різняться - TimeZoneInfoвідверто кажучи, більше, ніж зазвичай бажають розкрити. (Він не підтримує часовий пояс, уявлення про який "стандартний час" змінюється з часом або який переходить у постійний перехід на літній час.)
  • Навіть із базою даних zoneinfo ідентифікатори часових поясів не обов'язково стабільні. (CLDR звертається до цього; щось, що я сподіваюсь підтримати в Noda Time, зрештою.)
  • Текстові подання дат та часу - це кошмар, не тільки з точки зору впорядкування, але роздільники дат, роздільники часу та дивні речі, такі як родові назви місяців
  • Початок дня не завжди буває опівночі - наприклад, у Бразилії перехід на весняний перехід на літній час переводить настінні годинники з 23:59:59 вечора на 1 ранку
  • У деяких випадках (ну, той, про який я знаю) часовий пояс може змусити пропустити цілий день - 30 грудня 2011 року на Самоа не відбулося! Я підозрюю, що більшість розробників, можливо, можуть ігнорувати цей, але ...
  • Якщо ви збираєтесь використовувати календар, відмінний від григоріанського, будьте обережні та переконайтесь, що ви справді знаєте, як ви очікуєте від нього.

Що стосується конкретних практик розвитку:

  • Подумайте, що ви насправді намагаєтесь представляти. Я очікую, що основною перевагою Noda Time буде змусити розробників вибирати між різними типами для представлення своїх даних. Правильно зрозумійте, а все інше простіше.
  • Юніт-тест - все, що ви можете подумати. Звичайно, це залежатиме від того, що саме робить ваша система, але особливо враховуйте різні часові пояси, те, що відбувається в переходи на літній час, і звичайно високосні роки.
  • Я б порадив вводити "інтерфейс, схожий на годинник" - послугу для визначення поточного часу, а не явно телефонувати DateTime.Nowабо DateTime.UtcNow; це полегшує (можливо!) одиничне тестування
  • Якщо ви виконуєте кілька операцій із "зараз", отримайте цю дату / час один раз і запам'ятайте його, а не повторно просити "зараз" - інакше значення може змінюватися невдалим чином між дзвінками.
  • "Зробити все за UTC" теж не завжди є відповіддю - якщо я хочу знати, "коли саме в моєму місцевому часовому поясі трапляється" через два тижні "?" тоді мені потрібно зберегти місцеву дату / час, а також часовий пояс.

2
@flq: Знову ж таки, вам потрібно буде точно визначити, що ви маєте на увазі під терміном "високосний рік". Я сумніваюся, що ця помилка в фреймворку спричинила проблему в Azure - я сподіваюся, що вона використовувала неякісно .
Джон Скіт,

10
Немало пунктів реклами без жодного на тему високосного року (тобто теми). А потім надув у Twitter. Я думаю, у вас були кращі моменти, Джон.
Уілл Дін

8
@WillDean: Також зверніть увагу, що питання редагував Джордж Стокер. Перша назва було «Оборонна програмування проти DateTime помилок» - в цьому випадку , я думаю , ви погодитеся , мій пост є повністю актуальним. (Я тільки що помітив, що заголовок було змінено. На ньому було вказано DateTime, коли я почав відповідати ...)
Джон Скіт,

2
Джон, вибачте, я не бачив попереднього заголовка! Можливо, хтось повинен змінити заголовок, щоб також редагувати всі відповіді ...
Дін

3
@WillDean: Так ... У мене виникає спокуса або відмовитись від зміни заголовка, або зробити так, щоб це закінчилося "помилками DateTime (наприклад, високосні роки)"
Джон Скіт,

25

Варто зазначити, що помилка, ймовірно, не була пов’язана з таким рядком, як ви розмістили:

DateTime.Now.AddYears(1)

Це не створює недійсної дати. Якщо ви біжите:

(new DateTime(2012, 2, 29)).AddYears(1)

Ви отримуєте 28 лютого 2013 року. Я не знаю, в чому написаний запрошений агент Azure, але, мабуть, це був інший виклик, який не вдався. Поганим способом зробити це в .NET було б:

new DateTime(today.Year + 1, today.Month, today.Day)

Це створює виняток, якщо todayце високосний день. Однак у блозі Microsoft про проблему Azure сказано, що вони створили недійсну дату 29 лютого 2013 року, з якою я не впевнений, що це можливо зробити DateTimeв .NET.

Я не кажу про це DateTimeі DateTimeOffsetне схильний до помилок, просто не думаю, що вони спричинили б саме цю проблему.


Я здогадуюсь, що це було зроблено в C ++
PhilPursglove

2
@PhilPursglove: Що змушує вас це думати? ОС Windows була написана в основному на C і C ++ і належним чином обробляє високосні дні. Можливо, це було більше пов’язано з помилкою, пов’язаною з датою, у процесі створення сертифіката передачі.
In silico

Чи може це розбір дати з рядка?
Фіксатор

І так, ви мали рацію щодо AddYears (1), як ви вже зазначили. Я не впевнений, що відбувалося внутрішньо. Це була лише спроба надати питанню трохи перспективи, не заглиблюючись надто глибоко в блог.
Фіксатор

Це має сенс. Я, чесно кажучи, не знаю, чи так було, але це заслуговує довіри. Я бачив неприємний код від великих компаній, включаючи MS. Але знову ж таки, ми справді не можемо багато чого зробити, окрім спекуляцій.
Альфа

2

Як ми можемо розробити практики кодування, призначені для захисту від помилок високосного року? Які практики кодування могли б запобігти цьому?

Модульне тестування конкретних дат, як згадував Джон, є однією практикою коду, яка, однак, нічим не може перевершити те, що я визначаю як "тест інтеграції вручну"

змініть годинник на своєму сервері розробки / тестового стенду та спостерігайте, що відбувається, коли час минає

Не заглиблюйтесь у специфіку, чи це "практика кодування" - Очевидно, ви не можете робити це для кожної дати в календарі - виберіть дати, які вас стосуються, будь то 29 лютого, кінець місяця дати або перехід на літній час.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.