Работа с часовыми поясами и летнее время в Javascript
Одностраничное приложение javascript извлекает данные в формате JSON через вызовы REST. Даты форматируются с использованием часовой пояс UTC в стандартном формате ISO8601, например 2011-02-04T19:31:09Z
.
При регистрации на услугу пользователи выбирают свой часовой пояс из выпадающего списка. Этот часовой пояс может отличаться от часовой пояс пользовательского браузера. Приложение javascript знает, что пользователь выбрал часовой пояс.
Я знаю, как преобразовать строку UTC в дату. Я понимаю, что Javascript представляет только даты в локальном часовом поясе.
Но у меня возникают проблемы с выяснением того, как отображать дату, отформатированную для часового пояса OTHER, чем пользовательский часовой пояс. Он должен учитывать DST во все даты. Внутри я хочу иметь дело со всеми датами как UTC и только конвертировать в строковое представление даты в другой часовой пояс во время отображения. Мне нужно отображать даты в часовом поясе, выбранном в профиле пользователя, а не в часовом поясе браузера.
Я экспериментировал с сервером, который отправил разницу смещения часового пояса в миллисекундах между часовым поясом пользовательского браузера и часовым поясом пользовательского профиля. Но я обнаружил, что не могу просто отправить одно значение смещения, но вам нужно отправить смещение для каждой даты для учета изменений в DST.
Любые предложения или пример кода, как отображать даты, отформатированные в разных часовых поясах? Параметры, которые я нашел до сих пор:
- Сервер отправляет даты как строки, уже отформатированные в нужном часовом поясе, и на клиенте не выполняется синтаксический анализ или манипуляция датой. Это затрудняет выполнение некоторых вещей на клиенте, если не невозможно.
- Используйте библиотеку, такую как https://github.com/mde/timezone-js, которая включает всю базу данных Olson TZ в Javascript. Это означает, что время загрузки больше, чем использование памяти, и т.д.
- Отправляет часовой поясОфисное значение с каждой датой, отправленной клиенту. Это приводит к беспорядочным данным JSON и неоптимальным интерфейсам REST.
Есть ли более простые или лучшие решения?
Ответы
Ответ 1
2 - плохая идея, поскольку, как вы указали, это увеличивает время загрузки. Если бы я был вами, я бы сделал комбинацию 1 и 3. Я не согласен, что это делает данные JSON беспорядочными или интерфейс REST неоптимальным.
Это классический компромисс, где можно принять немного сложнее в протоколе, чтобы упростить клиентский код.
Ответ 2
Ускоренная перемотка вперед до 2015 года, когда дело доходит до форматирования даты из другого часового пояса, используя определенную локаль, есть два варианта.
В этом примере я использую французский язык и буду использовать (UTC + 5) дату 27 марта 2016 года, 2h15 , что не наблюдается в Западной Европе (из-за изменения DST часы перемещаются из 2h00 до 3h00), который является общим источником ошибок.
Для максимальной переносимости используйте библиотеку moment.js. Moment поставляется в комплекте 80+ локалей.
-
Использование временной метки и смещения UTC:
moment(1459026900000).utcOffset(300).locale('fr').format("LLLL")
// "dimanche 27 mars 2016 02:15"
-
Использование массива целых чисел (обратите внимание, что в момент, месяц основан на 0, как и в исходном объекте JS Date)
moment.utc([2016,3-1,27,2,15]).locale('fr').format("LLLL")
// "dimanche 27 mars 2016 02:15"
Вы можете протестировать эти методы, запустив код в инструментах браузера dev на http://momentjs.com/
Трудная часть здесь заключается в использовании moment.utc
, который создает объект с датой момента с флагом UTC, что означает, что когда эта дата отформатирована, она не будет преобразована в часовой пояс пользователя, а просто отображается как есть (и с UTC не наблюдает DST, он не подвержен ошибкам DST).
Будущее собственное решение: используя Intl
object
(обратите внимание, что на конец 2015 года это решение поддерживается Chrome, Firefox, IE11, но все еще не поддерживается в Safari 9)
Использование массива целых чисел:
new Intl.DateTimeFormat('fr-FR', { timeZone: 'UTC', weekday: 'long',
month: 'long', year: 'numeric', day: 'numeric', hour: 'numeric',
minute: 'numeric' })
.format(Date.UTC(2016,3-1,27,2,15))
// "dimanche 27 mars 2016 02:15"
Ключевым моментом здесь является использование Date.UTC
и timeZone: 'UTC'
, чтобы гарантировать, что предоставленная дата не будет преобразована в пользовательский локальный часовой пояс.
Обратите внимание, что в обоих случаях мы используем методы UTC, просто чтобы убедиться, что дата будет использоваться как -я без конверсий. Важно понимать, что эти даты являются фальшивыми датами UTC (они представляют время в заданном часовом поясе, а не время UTC) и должны использоваться только для форматирования и отображения даты - не должны передаваться.
Ответ 3
имеют одинаковые проблемы. # 1 - это решение, я думаю. Вы должны отправить все компоненты даты (год, месяц, день, часы) в специальный контейнер с клиента на сервер