Работа с часовыми поясами и летним временем в Javascript - PullRequest
22 голосов
/ 05 февраля 2011

Мое одностраничное приложение javascript извлекает данные в формате JSON посредством вызовов REST. Даты форматируются с использованием часового пояса UTC в стандартном формате ISO8601, например 2011-02-04T19:31:09Z.

При регистрации в службе пользователи выбирают свой часовой пояс из раскрывающегося списка. Этот часовой пояс может отличаться от часового пояса браузера пользователя. Приложение javascript всегда знает, какой часовой пояс выбран пользователем.

Я знаю, как преобразовать строку UTC в дату. Я понимаю, что Javascript представляет только даты в местном часовом поясе.

Но у меня возникают проблемы с выяснением , как отобразить дату, отформатированную для часового пояса ДРУГОЕ, чем локальный часовой пояс пользователя . Он должен учитывать DST на все даты. Внутренне, я хочу иметь дело со всеми датами как UTC и преобразовать только в строковое представление даты в другом часовом поясе во время отображения. Мне нужно отображать даты в часовом поясе, выбранном в профиле пользователя, а не в часовом поясе их браузера.

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

Есть предложения или пример кода о том, как отображать даты, отформатированные в разных часовых поясах? Варианты, которые я нашел до сих пор:

  1. Сервер отправляет даты в виде строк, уже отформатированных в правильном часовом поясе, и на клиенте не выполняется анализ или манипулирование датами. Это делает выполнение определенных действий на клиенте трудным, если не невозможным.
  2. Используйте библиотеку, такую ​​как https://github.com/mde/timezone-js,, которая включает всю базу данных Olson TZ в Javascript. Это означает увеличение времени загрузки, увеличение использования памяти и т. Д.
  3. Отправляет значение timezoneOffsetMillis с каждой датой, отправляемой клиенту. Это приводит к беспорядочным данным JSON и неоптимальным интерфейсам REST.

Есть ли более простые или лучшие решения?

Ответы [ 3 ]

5 голосов
/ 05 февраля 2011

2 - плохая идея, поскольку, как вы указали, она увеличивает время загрузки.На вашем месте я бы сделал комбинацию 1 и 3. Я не согласен с тем, что это делает данные JSON беспорядочными или интерфейс REST неоптимальным.

Это классический компромисс, в котором нужно принять битболее сложный протокол для упрощения кода на стороне клиента.

4 голосов
/ 04 ноября 2015

Перемотка вперед к 2015 году, когда дело доходит до форматирования даты из другого часового пояса с использованием определенной локали , есть две опции.

В этом примере я используюФранцузская локаль и будет использовать (UTC + 5) дату 27 марта 2016 года, 2 ч 15 , которая не наблюдается в Западной Европе (из-за изменения летнего времени , часы перемещаются с 2 ч 3 до 3 ч), который является общим источникомошибки.

Для максимальной переносимости используйте библиотеку moment.js .Момент наступает со связанными 80 + локалями .

  1. Использование отметки времени и смещения UTC:

    moment(1459026900000).utcOffset(300).locale('fr').format("LLLL")
    // "dimanche 27 mars 2016 02:15"
    
  2. Использование массивацелых чисел (обратите внимание, что в данный момент месяц основан на 0, как в собственном объекте JS Date)

    moment.utc([2016,3-1,27,2,15]).locale('fr').format("LLLL")
    // "dimanche 27 mars 2016 02:15"
    

Вы можете протестировать эти методы, запустив код в браузереdev tools on http://momentjs.com/

Сложность здесь заключается в использовании moment.utc, который создает объект даты-момента с флагом UTC, что означает, что при форматировании этой даты она не будет преобразована в пользователячасовой пояс, но отображается как есть (и поскольку UTC не наблюдает летнее время, он не подвержен ошибкам летнего времени).

Будущее собственное решение: использование 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) и должны использоваться только для форматирования и отображения даты - их не следует передавать.

0 голосов
/ 16 марта 2011

имеют ту же проблему. Я думаю, что №1 - это решение. Вы должны отправить все компоненты даты (год, месяц, день, часы) в специальном контейнере от клиента к серверу

...