19 November 2014

Решение проблем с RTZ2 после Microsoft Update KB2998527

Website developmentJavaScript
Sandbox
После выпуска упомянутого выше обновления многие разработчики столкнулись с проблемами. На текущий момент в Chrome проблему хоть как-то попытались исправить, а в IE10 (Document mode=«Standarts») все работает хорошо, но в более старых браузерах и режимах совместимости IE вся работа с датами развалилась.

Так сложилось, что из-за технических ограничений я вынужден поддерживать работу разрабатываемых мной приложений в IE8-9-10 и Chrome. Кроме того, для работы с датами использую библиотеку momentjs.

Поскольку дата релиза была за горами, то, поискав на просторах всемирной паутины решение проблемы и не найдя его, я решил, что рано или поздно разработчики браузеров решат эту проблему и мне не придется ничего менять. Но релиз всё ближе, а решения как не было, так и нет.

И вот настало время решить эту проблему самостоятельно.

Кратко опишу основные проблемы в виде таблицы (неожиданные результаты зачеркнуты):

IE10(IE8 standarts) IE9(all document modes) IE10(IE10 standarts) Chrome 38.0.2125.122 m
new Date(2014, 0, 1) Tue Dec 31 23:00:00 UTC+0300 2013 Wed Jan 1 00:00:00 UTC+0300 2014 Wed Jan 1 00:00:00 UTC+0400 2014 Wed Jan 01 2014 01:00:00 GMT+0400
new Date(2015, 0, 7) Tue Jan 6 23:00:00 UTC+0300 2015 Tue Jan 6 23:00:00 UTC+0300 2015 Wed Jan 7 00:00:00 UTC+0300 2015 Wed Jan 07 2015 01:00:00 GMT+0400
moment().startOf('year') Tue Dec 31 23:00:00 UTC+0300 2013 Wed Jan 1 00:00:00 UTC+0300 2014 Wed Jan 1 00:00:00 UTC+0400 2014 Wed Jan 01 2014 01:00:00 GMT+0400
moment().endOf('year') Wed Dec 31 22:59:59 UTC+0300 2014 Wed Dec 31 23:59:59 UTC+0300 2014 Wed Dec 31 23:59:59 UTC+0300 2014 Thu Jan 01 2015 00:59:59 GMT+0300

Первое, что пришло на ум — это доработать библиотеку работы с датами (momentjs) для корректного вычисления моментов времени и повсеместно её использовать вместо оригинального объекта Date или найти альтернативную библиотеку. Посмотрел datejs, xdate, tzdata-javascript. Но переписывать уйму кода для «временного» решения проблем показалось слишком трудоёмким. Всё таки надежда на то, что проблемы с временной зоной исчезнут со следующими патчами браузеров остается.

И тут пришла светлая мысль — поменять объект Date. Пусть он обрабатывает проблемные даты и приводит их к единому виду не зависящему от браузера. Т.е. new Date(2014, 0, 1) должен всегда создавать дату 01.01.2014 00:00 UTC+0400, тогда и momentjs.endOf('year') будет правильно считать конец года.

Как же этого добиться?

Принцип такой — время в UTC+0000 непрерывно и ошибки реализации временной зоны на него не влияют, поэтому все манипуляции с датой можно производить именно в UTC. И вообще игнорировать работу с временной зоной в браузере раз она работает неправильно. Но эта доработка должна быть прозрачной для пользователя (Web-разработчика), т.е. он по прежнему должен видеть дату в своей локальной временной зоне.

Изучив спецификацию объекта Date, разработал следующий способ:

1. Необходимо переопределить конструктор Date так, чтобы он создавал время в UTC.
Назовем оригинальный объект Date — NativeDate, а новый (исправленный) объект NewDate.
Тогда NewDate(year, month, date) на самом деле будет выполнять код NativeDate(NativeDate.UTC(year, month, date)).
2. Методы setTime, getTime и valueOf работают со смещением, т.е., если описать схематично, то NewDate.setTime(offset) выполняет NativeDate.setTime(offset+_nullOffset), а NewDate.getTime() и NewDate.valueOf() возвращают NativeDate.getTime()-_nullOffset. Где _nullOffset = -new NativeDate(0).getTimezoneOffset()*60000. Такой подход обеспечит прозрачную работу с нулевым смещением (new Date(0)).
3. Сеттеры и геттеры для свойств Date, Day, FullYear, Hours, Milliseconds, Minutes, Month, Seconds объекта NewDate делаем ссылками на сеттеры и геттеры аналогичных свойств с префиксом UTC объекта NativeDate. А сеттеры и геттеры с префиксом UTC для NewDate будут учитывать временную зону.
После выполнения этих действий всё заработало просто отлично.

Однако для полной поддержки функциональности оригинального объекта Date потребовалось поддержать все варианты вызова конструктора Date, а также методы Date.parse, Date.UTC, Date.now и все возможные способы сериализации объекта в строку.

Получилось как-то так: rtz2fix.

Буду очень рад, если кто-то обнаружит проблемы в данном решении и сообщит о них мне.

Ссылки по теме


«Хром, укравший рождество»;
«Новая таймзона — новые проблемы».
Tags:javascriptrtzchromeieKB2998527momentjs
Hubs: Website development JavaScript
+13
20.6k 51
Comments 15
Popular right now