Разрабатываем мы тут некоторый сервис интеграции с очень сторонней системой. Сам сервис работает на Node.js. И всё бы хорошо, но только недоступность сервера во время сборки мусора очень нервировала стороннюю систему.
В канун нового года было решено сделать серверу подарок — обновить Node.js с версии 0.4.8 до 0.6.6 В силу ряда организационных причин, обсуждать которые здесь не очень хочется, обновление было проведено сразу на боевой системе и даже без регрессионного тестирования.
Неужели в этой ситуации что-то могло пойти не так?
Обновились. Работаем дальше. Внезапно выясняется, что в передаваемых сторонней системой сообщениях время оказывается сдвинуто на 4 часа вперёд. О бизнес-последствиях такого сдвига рассказывать, наверное, не нужно.
Начинаем думать. Возникает гипотеза, что если всё раньше работало, а с обновлением Node.js вдруг перестало, то значит дело в нём или в v8. Не может быть, говорю. Чтобы такой косяк и мы первые заметили — наверняка это наш админ напортачил. Явно, говорю, у него что-то не так с установками часовой зоны на сервере. Заглянули во все возможные закоулки — нет, всё чисто.
Осталась последняя, самая невероятная гипотеза — сломался разбор времени в формате ISO 8601. Именно в этом формате сторонняя система и присылает время в сообщениях. Казалось бы, ну что тут можно сломать. Вот приходит локальное время: “2011-12-30T22:00:00”. По-быстрому смотрим в сервере:
Опаньки. Сервер явно живёт по Гринвичу. Не иначе, уже эмигрировал. Проверяем:
В обоих случаях, вне зависимости от того, указывается ли время локально или по Гринвичу, оно воспринимается как по Гринвичу. Смотрим, что на это скажет Хром:
А вот это уже неприятно. Хром — на рабочей станции. И с часовой зоной там всё в порядке.
Пока разработчик теребит меня вопросом “Когда уже баг на гугл постить будем?” я читаю описание стандарта. 130 франков на официальный документ нет, поэтому изучаем Википедию:
Копаем дальше.
-4 возвращается на всех тестируемых компьютерах и платформах. Тут мне в голову приходит мысль. До сих пор мы проверяли везде кроме Firefox и Windows. Смотрю в FF под Windows:
Проверяю то же самое в Хроме под Windows:
Вот это да! Неужели в самом деле у гугла косяк? Снова поступает предложение написать баг-репорт. Сочинять его пока ещё лень, поэтому я начинаю читать руководства.
Mozilla Developer Network (MDN). Описание метода Date.parse:
Здесь время разбирается по стандарту, поэтому у Firefox всё правильно.
На v8 самостоятельной документации нет, зато есть ссылка на ECMAscript. Скачиваем спецификацию версии 5.1, на странице 181 читаем:
Ничего себе! Сторонняя система передаёт нам локальное время в соответствии со стандартом ISO, а наш сервер интерпретирует его как по Гринвичу — в полном соответствии со стандартом ECMA.
На всякий случай читаю MSDN — Microsoft ведь издавна утверждала, что их стандарт JavaScript самый правильный, потому что он ECMA. И точно:
Изучение репозитариев показало, что то самое исправление поведения v8 было внесено в редакции 8513 в конце июня 2011. Однако, когда Мозилле было предложено исправить это у себя, в дискуссии было указано на разночтения в стандартах ISO и ECMA 5.1.В результате, есть шанс, что в версии 6 стандарта ECMA время будет таки разбираться правильно.
А нам пока пришлось добавить в код костылей. Если время, приходящее от сторонней системы, не содержат указания на часовую зону, добавляем к нему getTimezoneOffset. После того как выйдет новый стандарт ECMA, в котором это разночтение будет исправлено и v8 обновится в соответствии с этим стандартом, нам придется это отследить и костыли убрать.
P.S. Основной удар в описываемых событиях принял на себя хабраюзер zerodivisi0n, у которого, к сожалению, есть права только на чтение. Если у кого-то хватит кармы (или чего там необходимо), чтобы перевести его в более продвинутое состояние, мы оба будем весьма признательны. Заодно он сам и на вопросы сможет ответить.
В канун нового года было решено сделать серверу подарок — обновить Node.js с версии 0.4.8 до 0.6.6 В силу ряда организационных причин, обсуждать которые здесь не очень хочется, обновление было проведено сразу на боевой системе и даже без регрессионного тестирования.
Неужели в этой ситуации что-то могло пойти не так?
Обновились. Работаем дальше. Внезапно выясняется, что в передаваемых сторонней системой сообщениях время оказывается сдвинуто на 4 часа вперёд. О бизнес-последствиях такого сдвига рассказывать, наверное, не нужно.
Начинаем думать. Возникает гипотеза, что если всё раньше работало, а с обновлением Node.js вдруг перестало, то значит дело в нём или в v8. Не может быть, говорю. Чтобы такой косяк и мы первые заметили — наверняка это наш админ напортачил. Явно, говорю, у него что-то не так с установками часовой зоны на сервере. Заглянули во все возможные закоулки — нет, всё чисто.
Осталась последняя, самая невероятная гипотеза — сломался разбор времени в формате ISO 8601. Именно в этом формате сторонняя система и присылает время в сообщениях. Казалось бы, ну что тут можно сломать. Вот приходит локальное время: “2011-12-30T22:00:00”. По-быстрому смотрим в сервере:
> var d = new Date('2011-12-30T22:00:00') undefined > d Fri, 30 Dec 2011 22:00:00 GMT > d.getHours() 2
Опаньки. Сервер явно живёт по Гринвичу. Не иначе, уже эмигрировал. Проверяем:
> var d = new Date('2011-12-30T22:00:00Z') undefined > d Fri, 30 Dec 2011 22:00:00 GMT > d.getHours() 2
В обоих случаях, вне зависимости от того, указывается ли время локально или по Гринвичу, оно воспринимается как по Гринвичу. Смотрим, что на это скажет Хром:
> var d = new Date('2011-12-30T22:00:00') undefined > d.toString() "Sat Dec 31 2011 02:00:00 GMT+0400 (MSK)" > d.getHours() 2
А вот это уже неприятно. Хром — на рабочей станции. И с часовой зоной там всё в порядке.
Пока разработчик теребит меня вопросом “Когда уже баг на гугл постить будем?” я читаю описание стандарта. 130 франков на официальный документ нет, поэтому изучаем Википедию:
If no UTC relation information is given with a time representation, the time is assumed to be in local time
Копаем дальше.
var d = new Date('2011-12-30T22:00:00') undefined d. getTimezoneOffset()/60 -4
-4 возвращается на всех тестируемых компьютерах и платформах. Тут мне в голову приходит мысль. До сих пор мы проверяли везде кроме Firefox и Windows. Смотрю в FF под Windows:
var d = new Date('2011-12-30T22:00:00') undefined d.getUTCHours() 18 d.getHours() 22
Проверяю то же самое в Хроме под Windows:
var d = new Date('2011-12-30T22:00:00') undefined d.getUTCHours() 22 d.getHours() 2
Вот это да! Неужели в самом деле у гугла косяк? Снова поступает предложение написать баг-репорт. Сочинять его пока ещё лень, поэтому я начинаю читать руководства.
Mozilla Developer Network (MDN). Описание метода Date.parse:
If you do not specify a time zone, the local time zone is assumed
Здесь время разбирается по стандарту, поэтому у Firefox всё правильно.
На v8 самостоятельной документации нет, зато есть ссылка на ECMAscript. Скачиваем спецификацию версии 5.1, на странице 181 читаем:
The value of an absent time zone offset is «Z».
Ничего себе! Сторонняя система передаёт нам локальное время в соответствии со стандартом ISO, а наш сервер интерпретирует его как по Гринвичу — в полном соответствии со стандартом ECMA.
На всякий случай читаю MSDN — Microsoft ведь издавна утверждала, что их стандарт JavaScript самый правильный, потому что он ECMA. И точно:
If you do not include a value in the Z position, UTC time is used.
Изучение репозитариев показало, что то самое исправление поведения v8 было внесено в редакции 8513 в конце июня 2011. Однако, когда Мозилле было предложено исправить это у себя, в дискуссии было указано на разночтения в стандартах ISO и ECMA 5.1.В результате, есть шанс, что в версии 6 стандарта ECMA время будет таки разбираться правильно.
А нам пока пришлось добавить в код костылей. Если время, приходящее от сторонней системы, не содержат указания на часовую зону, добавляем к нему getTimezoneOffset. После того как выйдет новый стандарт ECMA, в котором это разночтение будет исправлено и v8 обновится в соответствии с этим стандартом, нам придется это отследить и костыли убрать.
P.S. Основной удар в описываемых событиях принял на себя хабраюзер zerodivisi0n, у которого, к сожалению, есть права только на чтение. Если у кого-то хватит кармы (или чего там необходимо), чтобы перевести его в более продвинутое состояние, мы оба будем весьма признательны. Заодно он сам и на вопросы сможет ответить.