Мои выводы после внедрения системы бронирования встреч по календарю с использованием JavaScript, Moment.js и PostgreSQL.

Прошел год с тех пор, как я унаследовал и завершил проект Javascript Booking System, но он продолжал преподносить сюрпризы все это время. В этом посте я хотел бы поделиться с вами некоторыми рекомендациями, которые следует учитывать при работе с датами.

Три основные проблемы, с которыми вы можете столкнуться при работе с датами JavaScript, — это часовой пояс, UTC и летнее время, давайте подробнее рассмотрим каждую из них.

Распространенные проблемы с часовым поясом

Вывод даты в вашем часовом поясе кажется довольно простым. Однако, вероятно, первый вопрос, который возникнет при работе над этим проектом: Должен ли я сохранять дату с часовым поясом пользователя? Итак, это зависит от вашего проекта, но мой вывод: никогда не сохраняйте даты в часовом поясе пользователя. часовой пояс. Я считаю хорошей практикой всегда преобразовывать дату в формат UTC и сохранять преобразованное значение в базу данных. Убедитесь также, что в вашей базе данных есть отдельный столбец для хранения события часовой пояс (в моем случае это часовой пояс пользователя, создавшего событие). При извлечении даты встречи UTC из базы данных преобразуйте эту дату обратно в локальный часовой пояс события, например:

//good
moment().utc().format(); // Convert date into UTC and save value
moment.tz('2021-02-13T05:31:18Z', 'America/New_York').format(); // Convert back to user's timezone and display on frontend

Распространенные проблемы с UTC — «Путешествие во времени»

Я почти уверен, что у вас была такая ситуация, когда вы отправляете заказ или какую-либо заявку онлайн после 8 или 9 вечера, и вы видите, что дата отправки отображается с завтрашней датой. Внутри компании мы называем это "путешествием во времени". Эта проблема обычно возникает, если вы:

  1. сохранить дату UTC в БД как YYYY-MM-DD вместо полной отметки времени в UTC. например
// wrong
'2021-02-13'
// good
'2021-02-13T05:31:18Z'

Если вы конвертируете отметку времени в UTC и получаете только date часть без time (YYYY-MM-DD), дата попадет на следующий день (смещение +1 день), что приведет к вставке неправильной даты в базу данных, например, попробуйте console.log() это :

// bad
moment('2021-02-13T19:15:18-05:00').utc().format('YYYY-MM-DD');
// output will be '2021-02-14' instead of '2021-02-13' we can see +1 day offset
// good
moment('2021-02-13T19:15:18-05:00').utc().format();

2. Другой вариант использования: если вы сохранили полную метку времени в формате UTC, но при чтении из базы данных вы усекаете временную часть (обычно на внешнем интерфейсе). Результат будет таким же, как и в предыдущем примере, у вас будет смещение даты + 1 день, но на этот раз ошибка находится в вашей внешней среде.

Решение. Всегда сохраняйте в базу данных полную дату с отметкой времени в UTC и конвертируйте дату UTC обратно в часовой пояс пользователя, а затем усекайте time часть, например:

// good
moment('2021-02-14T00:27:58Z').tz('America/New_York').format('YYYY-MM-DD'); // output is 2021-02-13 - converted back to user's timezone

Проблема перехода на летнее время — летнее время

Мы запустили игру, не имея много времени на тестирование, и это всегда отбрасывает вас назад. После перехода на летнее время (далее DST) у нас возникла одна большая проблема. Все повторяющиеся встречи были смещены на 1 час, упс. Я начал отлаживать и вот что я нашел:

Диапазоны

Когда я заглянул в базу данных, я понял, что startDate и endDate были сохранены как диапазон repeat_range в базе данных, но основная проблема в том, что они были сохранены как date только строки без time. Однако в другом столбце они сохранили meeting_time в виде строк. Когда мы извлекали данные из базы данных, эти 2 строки были объединены, и без знания фактического часового пояса изменение летнего времени не применялось к ним, что вызывало смещение +-1 час. Чтобы пост был коротким, я опубликую только то, что нужно было сделать в начале, чтобы избежать этой ситуации, вместо того, чтобы повторять все шаги, которые я предпринял для исправления данных.

// bad
['2010-01-01','2021-01-01')
// good
['2010-01-01T14:45:00Z','2021-01-01T15:45:00Z') //yyyy-MM-dd’T’HH:mm:ssZ

Решение. Всегда сохраняйте в базе данных полную дату с отметкой времени и информацией о часовом поясе. например

Сводка

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

Рекомендации:

  • Я определенно рекомендую использовать временную метку UTC для обозначения определенного момента времени.
  • Всегда сохраняйте в базе данных часовой пояс пользователя, который впоследствии используется для преобразования события в его местное время.
  • Никогда не сохраняйте дату (YYYY-MM-DD) без времени (HH:mm:ss), используйте полную временную метку yyyy-MM-dd’T’HH:mm:ssZ
  • При использовании диапазонов используйте полную метку времени yyyy-MM-dd’T’HH:mm:ssZ и не храните дату (YYYY-MM-DD) и время (HH:mm:ss) в отдельных столбцах, чтобы избежать проблемы DST.

Даже если поначалу это кажется сложным. Я хотел бы подчеркнуть, что для простоты всегда работайте со временем UTC до тех пор, пока оно не будет отображаться пользователю.

Удачного взлома!