Мой клиент, находящийся в страховом полисе, требует дату рождения потенциального застрахованного. Он вводится в веб-форму с использованием jQuery datepicker, подключенного к свойству модели с использованием Knockout и отправленного через ajax в JSON в контроллер MVC 4.
Некоторые страхователи получили свою страховую документацию с неправильной датой рождения. Во время последующего расследования мы обнаружили, что в дополнение к ошибкам ввода данных, которые являются чрезвычайно маргинальными, неправильные даты сосредоточены в следующие два периода:
- последние три недели с марта по начало апреля
- последняя неделя октября до первой недели ноября.
Поскольку наш клиент находится в часовом поясе Америки/Монреаля, я сразу же подумал о проблеме DST. Правила DST изменились в 2007 году в этом часовом поясе.
Читая несколько статей и другие вопросы о переполнении стека, я узнал, что в стандарте ECMAScript 5 указано, что реализация должна учитывать текущие правила DST и не должна учитывать историю изменений DST. ES5 15.9.1.8 Единственный браузер, не соответствующий этой спецификации на данный момент, - IE10 (подробнее об этом позже).
Учитывая эту спецификацию, браузеры сообщают о датах следующим образом (проверено в Chrome):
(new Date(2006, 9, 31, 0, 0, 0)).toISOString()
// 2006-10-31T04:00:00.000Z
(new Date(2008, 9, 31, 0, 0, 0)).toISOString()
// 2008-10-31T04:00:00.000Z
В то время как платформа .NET сообщает о датах правильно:
DateTime dt = new DateTime(2006, 10, 31, 0, 0, 0);
Console.WriteLine("{0:MM/dd/yy H:mm:ss zzz}", dt);
// 10-31-06 0:00:00 -05:00
dt = new DateTime(2008, 10, 31, 0, 0, 0);
Console.WriteLine("{0:MM/dd/yy H:mm:ss zzz}", dt);
// 10-31-08 0:00:00 -04:00
Одна из причин этой проблемы заключается в том, что если застрахованный человек родился в последнюю неделю октября до 2007 года, время UTC будет сообщено моему контроллеру MVC неправильно, например:
Объект даты Javascript:
new DateTime(2006, 9, 31, 0, 0, 0);
.Net анализирует данные JSON и получает:
10-30-06 23:00:00 GMT
В ходе тестов я обнаружил, что внутреннее представление объекта Date в JavaScript несовместимо с внутренним DateTime.Net, и я не смог свернуть собственный парсер с использованием представления JavaScript:
Javascript:
(new Date(2006, 9, 31, 0, 0, 0)).getTime()
// 1162267200000
.Net:
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(1162267200000);
Console.WriteLine("{0}", dt);
// 2006-10-31 04:00:00
(Это неправильно, потому что он был представлен 30 октября 2006 года в 23:00 по местному времени.)
Для моего клиента используйте случай, так как это интересующая меня часть даты, я мог бы просто установить временную часть объекта Date в полдень, что защитит меня от всех случаев неправильной интерпретации даты на части JavaScripts. К сожалению, это Ugly Patch, и он не решает других потенциальных ситуаций, например, если мой клиент хотел, чтобы мы реализовали функциональность, требуя, чтобы мы имели точную дату и время события, которое происходило в прошлом.
Другим подходом было бы написать алгоритм для обнаружения потенциально проблемных недель DST в моем контроллере и установить правильное время, если я получу дату в течение рассматриваемых недель. К сожалению, учитывая, что в стандарте ECMAScript 6 указано, что история изменений DST должна соблюдаться (так что все будущие браузеры должны правильно обрабатывать эту ситуацию), и учитывая, что IE10 уже работает правильно, я боюсь создать проблемную ситуацию в будущем, Подход также кажется неправильным, архитектурно говоря.
Еще один аспект, который следует учитывать, заключается в том, что если мне нужно передать модель от клиента к серверу, а затем вернуться к клиенту, мне нужно будет создать способ воссоздать "плохой" объект JavaScript JavaScript, чтобы не влиять на отображение на клиентской стороне приложения.
Никакое решение не кажется правильным и расширяемым. Я не могу поверить, что раньше никогда не приходилось сталкиваться с этой проблемой.
Как я могу исправить это навсегда и таким образом, который можно было бы применить ко всем другим проектам JavaScript/MVC?
РЕДАКТИРОВАТЬ # 1 - Дополнительная информация, не связанная напрямую с проблемой:
Как заметил @matt-johnson, редко используется случай, когда информация о часовом поясе должна использоваться. В этом случае, однако, застрахованная дата рождения относится к часовому поясу, в который входит мой клиент. Пусть возьмут, например, 17-летнего жителя Виктории-BC с днем рождения 2 декабря. Если он отправит страховую котировку 1 декабря в 22:00, хотя он все равно 17, страховая котировка будет принята, потому что он уже 18 в моем часовом поясе клиента. Это же правило применяется для страхового ценообразования. Это юридическое требование.
Чтобы быть ясными, я ищу универсальное решение, которое может быть применено к любым другим проектам. Конкретная проблема: Javascript, всего за несколько конкретных недель в течение многих лет до 2007 года, сообщает время с 1-часовым смещением. Это смещение всегда присутствует независимо от того, как я представляю время (локальный часовой пояс или UTC), потому что это неверные данные (а не данные).
Я использовал метод "установить временную часть в полдень", чтобы обойти ошибку, но основная проблема все еще остается. Что произойдет, если мой клиент попросит нас разработать веб-приложение, требующее, чтобы мы получили дату и время определенного события в прошлом? Например: "Пожалуйста, укажите точную дату и время аварии: 31 октября 2005 года в 19:32". Контроллер MVC установил время в 18:32.