Microsoft только что объявила, что ошибка программного обеспечения при вычислении дат (в течение високосного года)
Как мы можем разработать методы кодирования, предназначенные для защиты от ошибок високосного года?
Ответ 1
Бесстыдный штекер:
Использовать лучший API с датой и временем
Встроенные библиотеки даты и времени .NET ужасно трудны в использовании. Они позволяют вам делать все, что вам нужно, но вы не можете четко выразить себя через систему типов. DateTime
является беспорядком, DateTimeOffset
может усыпить вас мыслью, что вы фактически сохраняете информацию о часовом поясе, когда вы этого не сделали, и TimeZoneInfo
не заставляет вас думать обо всем, что вы должны учитывать.
Ни один из них не дает хороший способ сказать "просто время суток" или "просто дату" и не проводить четкое различие между "местным временем" и "временем в конкретном часовом поясе". И если вы хотите использовать календарь, отличный от григорианского, вам нужно все время проходить через класс Calendar
.
Все это почему я строю Noda Time - альтернативная библиотека даты и времени, построенная на порту Joda Time "движок", но с новым (и более компактным) API сверху.
Некоторые моменты, о которых вы можете подумать, которые легко пропустить, если вы не знаете о них:
- Сопоставление локальной даты/времени с одним в конкретном часовом поясе не так просто, как вы могли бы подумать. Конкретная локальная дата/время может возникать один раз, дважды (двусмысленность) или нулевое время (оно пропускается) из-за перехода на летнее время.
- Временные зоны меняются исторически - более чем
TimeZoneInfo
, как правило, охотно раскрывает, честно говоря. (Он не поддерживает часовой пояс, чья идея "стандартного времени" изменяется со временем или которая переходит в постоянное летнее время.) - Даже в базе данных zoneinfo идентификаторы часовых поясов не обязательно стабильны. (CLDR адресует это, то, что я надеюсь поддержать в Нода-Времени в конце концов.)
- Текстовые представления дат и времени - это кошмар, а не только с точки зрения заказа, но разделители дат, разделители времени и нечетные вещи, такие как имена родительного месяца.
- Начало дня - не всегда полночь - в Бразилии, например, переход на летнее время spring перемещает настенные часы с 11:59:59 вечера до 1 часа.
- В некоторых случаях (ну, я знаю) часовой пояс может заставить пропустить целый день - 30 декабря 2011 года не произошло на Самоа! Я подозреваю, что большинство разработчиков могут игнорировать этот, но...
- Если вы собираетесь использовать календарь, отличный от григорианского, будьте осторожны и убедитесь, что вы действительно знаете, как вы ожидаете, что он будет себя вести.
Что касается конкретных методов разработки:
- Подумайте о том, что вы действительно пытаетесь представить. Я ожидаю, что основное преимущество Noda Time будет заставлять разработчиков выбирать между различными типами для представления своих данных. Получите это правильно, и все остальное проще.
- Unit test все, что вы можете придумать. Конечно, это будет зависеть от того, что делает ваша система, но, в частности, учитывать разные часовые пояса, что происходит в переходных периодах перехода на летнее время и, конечно, в високосные годы.
- Я бы посоветовал вводить "тактико-подобный интерфейс" - службу для указания текущего времени - вместо явного вызова
DateTime.Now
илиDateTime.UtcNow
; это упрощает (возможно!) до unit test - Если вы выполняете несколько операций с "сейчас", получите эту дату/время один раз и запомните это, а не повторно запрашивая "сейчас" - в противном случае значение может измениться неудачными способами между вызовами.
- "Делать все в UTC" не всегда является ответом - если я хочу знать ", когда именно" через две недели "происходит в моем местном часовом поясе?" то мне нужно сохранить локальную дату/время, а также часовой пояс.
Ответ 2
Стоит отметить, что ошибка, вероятно, была не из-за строки, которую вы опубликовали:
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
не подвержены ошибкам, просто я не думаю, что они вызвали бы эту конкретную проблему.
Ответ 3
Как мы можем разработать методы кодирования, предназначенные для защиты от ошибок високосного года? Какие методы кодирования могли бы предотвратить это?
Единичное тестирование конкретных дат, как упоминалось в Джоне, - это одна практика кода, которая поможет, однако, ничто не сравнится с тем, что я определяю как "тест ручной интеграции"
изменить часы на вашем сервере разработки/тестирования и посмотреть, что происходит, когда время тикает.
Не увязывайте специфику, является ли это "практикой кодирования". Очевидно, что вы не можете сделать это для каждой даты в календаре - выберите даты, о которых вы беспокоитесь, будь то 29 февраля, месячные даты или даты перехода на летнее время.