Правило большого пальца: не используйте его!

В resume.io — онлайн-конструкторе резюме, мы создали веб-сервис с нуля, и на данный момент это один из лучших опытов, которые я когда-либо получал как разработчик. Однако недавно в папке Входящие службы поддержки появилось несколько странных писем. Люди говорили нам, что не могут изменить даты своего трудового стажа и что система сама меняет правильные даты.

Я несколько раз пытался воспроизвести баг, даже тестировал его в точно такой же среде (благо у вас больше нет необходимости держать виртуальную машину на своем ноутбуке для этого), но это было безнадежно — интерфейс работал как это должно было сделать. Я заметил, что эти электронные письма были в основном от пользователей из США, поэтому я быстро предположил, что это может быть как-то связано с их часовым поясом. Значит это было!

Наше приложение было построено на React/Redux с Redux Form, и мы написали собственный компонент ‹Field /› для диапазонов дат. За исключением обычных дат, нам нужно было обрабатывать особые случаи, такие как не показывать дату или показывать только год, поэтому мы использовали адаптер, который преобразовывал данные в удобный для пользовательского интерфейса формат и преобразовывал их обратно при изменении значения. . Это удобно для тестирования и, к счастью (или к сожалению), приводит к некоторым забавным результатам, если одна часть преобразования сломана.

так в чем была проблема?

Оказалось, что мы передали даты как строки «2016–10–01»из нашего бэкэнда Rails вDate.parse функция:

MDN предупреждает нас, что использование этой функции не рекомендуется, поскольку она зависит от браузера и может привести к неожиданному поведению.

Причина в том, что браузеры по-разному относятся к часовым поясам. Например, Chrome и Safari предполагают, что время указано в формате UTC, тогда как другие ожидают, что оно будет в локальном формате. Попробуйте извлечь день месяца, и вы заметите разницу.

Кстати, если вы раньше работали с Date в ванильном JavaScript, вы, вероятно, заметили, что месяц начинается с нуля (функция getMonth вернет целое число от 0 до 11), что делает все еще забавнее.

Ирония судьбы: поскольку наша команда разработчиков находится в московском часовом поясе (сейчас GMT+03, а Date.parse всегда выдает один и тот же день), мы просто не могли обнаружить ошибку с самого начала.

Как избежать этих ужасных ошибок в будущем?

Вы никогда не должны использовать Date.parsefunction. Существует версия конструктора Date, которая вместо этого принимает целые числа года, месяца и дня:

Вы также можете использовать внешнюю библиотеку для анализа даты. В нашем проекте уже был moment.js, но учтите, что это довольно жирная зависимость.

Наконец, рассмотрите возможность использования временных меток. Самое главное — напишите приемочные тесты для ваших интерфейсов.

Заключение

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

Об авторе

Алексей Тактаров — основатель и технический директор компании Resume.io, а также ведущий разработчик в небольшой команде дизайнеров и разработчиков Эти ребята, где он работает над сложными одностраничными приложениями и MVP. Алекс пишет на Ruby и JavaScript, а его швейцарский нож — это Ruby on Rails, React, Redux и Redux Saga.

Тем временем он руководит сообществом Code Hipsters и делает крутые визуализации для ficus.io.

Женат, имеет трех кошек.