Работа с часовыми поясами и летнее время в 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 - это решение, я думаю. Вы должны отправить все компоненты даты (год, месяц, день, часы) в специальный контейнер с клиента на сервер