Что означает kst во времени

Работа с датой и часовыми поясами в JavaScript

От переводчика

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

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

Статья немаленькая и делится логически на две части:

Детальная информация о принятом сегодня стандарте времени и часовых поясов, которая будет полезна для разработчика на любом языке

Обзор подхода к работе со временем, принятого в JS, объекта Date, его методов и лучших практик в клиент-серверной разработке, и вывод почему для полноценной работы с датой вам не обойтись без задействования специальных библиотек.

Обработка часового пояса в JavaScript

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

Однако моя надежда не оправдалась, и продвигаясь в решении задачи, я все больше осознавал, насколько непросто обрабатывать часовой пояс в JavaScript. Реализация функций часовых поясов помимо простого форматирования времени и вычисления временных данных с помощью сложных операций (например, календаря) была поистине сложной задачей. По этой причине я приобрел бесценный опыт решения проблемы, который привел к возникновению новых проблем.

Что такое часовой пояс (time zone)?

GMT, UTC и Offset

Когда система еще находилась в разработке, англоязычное население хотело назвать систему CUT (всемирное координированное время), а франкоязычное население хотело назвать ее TUC (Мировое время). Однако ни одна из сторон не выиграла бой, поэтому они пришли к соглашению об использовании аббревиатуры UTC, поскольку она содержала все основные буквы (C, T и U).

Полный список смещений UTC и их названия можно найти здесь: Список смещений времени UTC.

Например, до 2006 года в США и Канаде летнее время начиналось с первого воскресенья апреля в 02:00 и длилось до последнего воскресенья октября в 12:00, а с 2007 года стало начинаться во второе воскресенье марта с 02:00 и длиться до 2:00 первого воскресенья ноября. В европейских странах летнее время применяется единовременно по всей стране, в то время как в США летнее время поочередно применяется к часовым поясам.

Меняется ли часовой пояс?

Как я вкратце упомянул ранее, каждая страна имеет собственное право определять, какой часовой пояс использовать, а это означает, что ее часовой пояс может быть изменен по любым политическим или экономическим причинам. Например, в штатах период перехода на летнее время был изменен в 2007 году, поскольку президент Джордж Буш подписал энергетическую политику в 2005 году. Египет и Россия использовали летнее время, но перестали его использовать с 2011 года.

1 часовой пояс : N смещений

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

Это не большая проблема в повседневной жизни, но эта проблема возникает при попытке систематизировать работу со временем на основе определённых правил. Представим, что вы хотите установить стандартное время для своего смартфона с помощью смещения (offset). Если вы живете в регионе, где применяется летнее время, время на вашем смартфоне следует корректировать всякий раз, когда летнее время начинается и заканчивается. В этом случае вам понадобится концепция, которая объединяет стандартное время и летнее время в один часовой пояс (например, Тихоокеанское время).

Но это не может быть реализовано с помощью пары простых правил. Например, поскольку штаты изменили даты начала и окончания летнего времени в 2007 г., 31 мая 2006 г. вам следовало жить по PDT (тихоокеанское летнее время, UTC-07:00 ), а после 31 марта 2007 г. уже по PST (стандартное тихоокеанское время, UTC-08:00 ). Это означает, что для обращения к определенному часовому поясу вы должны знать все исторические данные о стандартных часовых поясах или момент времени, когда правила летнего времени были изменены.

База данных часовых поясов IANA

База данных часовых поясов IANA поддерживается многочисленными сообществами разработчиков и историков. Новые исторические факты и политические решения сразу же попадают в базу данных, что делает ее наиболее надежным источником. Более того, многие ОС на базе UNIX, включая Linux и macOS, а также популярные языки программирования, включая Java и PHP, используют эту базу данных.

Обратите внимание, что Windows отсутствует в приведенном выше списке поддержки. Это потому, что Windows использует собственную базу данных под названием Microsoft Time Zone Database. Однако эта база данных неточно отражает исторические изменения и поддерживается только Microsoft. Следовательно, она менее точна и надежна, чем IANA.

JavaScript и База данных часовых поясов IANA

В зависимости от реализации, алгоритм использует наилучшую доступную информацию о часовых поясах для определения местной поправки на летнее время DaylightSavingTA (t), измеряемой в миллисекундах. Ожидается, что реализация ECMAScript сделает все возможное, чтобы определить местное летнее время.

Похоже, он просто говорит: «Эй, ребята, попробуйте и сделайте все возможное, чтобы это сработало». Это создает проблемы в кроссбраузерности. Вы можете подумать: «Это небрежно!», и тогда вы заметите еще одну строчку прямо под ним:

ПРИМЕЧАНИЕ. рекомендуется использовать информацию о часовых поясах из базы данных часовых поясов IANA http://www.iana.org/time-zones/.

Часовой пояс в серверно-клиентской среде

Мы предположим простой сценарий, в котором необходимо учитывать часовой пояс. Допустим, мы собираемся разработать простое приложение-календарь, которое будет обрабатывать информацию о времени. Когда пользователь вводит дату и время в поле на странице реестра в клиентской среде, данные передаются на сервер и сохраняются в БД. Затем клиент получает зарегистрированные данные расписания с сервера и отображает их на экране.

Однако здесь есть то, что нужно учесть. Что, если некоторые из клиентов, обращающихся к серверу, находятся в разных часовых поясах? Расписание, зарегистрированное на 11 марта 2017 г. в 11:30 в Сеуле, должно отображаться как 10 марта 2017 г. в 21:30 при просмотре расписания в Нью-Йорке. Чтобы сервер поддерживал клиентов из разных часовых поясов, расписание, хранимое на сервере, должно иметь абсолютные значения, на которые не влияют часовые пояса. Каждый сервер имеет свой способ хранения абсолютных значений, и это выходит за рамки данной статьи, поскольку все зависит от сервера или среды базы данных. Однако для того, чтобы это работало, дата и время, передаваемые от клиента на сервер, должны быть значениями, основанными на том же смещении (обычно в формате UTC) или значениями, которые также включают данные часового пояса клиентской среды.

Если вы работаете с этим с помощью JavaScript в среде браузера, вы должны преобразовать введенное значение, как описано выше, а затем преобразовать его обратно в соответствии с часовым поясом пользователя. Необходимо учитывать обе эти две задачи. В языке программирования первый называется «синтаксическим анализом», а второй «форматированием». Теперь давайте выясним, как это обрабатывается в JavaScript.

Даже когда вы работаете с JavaScript в серверной среде с использованием Node.js, возможно вам может потребоваться проанализировать данные, полученные от клиента. Однако, поскольку серверы обычно синхронизируют свой часовой пояс с базой данных, а задача форматирования обычно предоставляется клиентам, вам нужно учитывать меньше факторов, чем в среде браузера. В этой статье мы будем рассматривать примеры в среде браузера.

Объект даты в JavaScript

В JavaScript задачи, связанные с датой или временем, обрабатываются с помощью объекта Date. Это собственный объект, определенный в ECMAScript, также, как Array или Object. Он в основном реализован в собственном коде, таком как C++. Его API хорошо описан в документации MDN. На это большое влияние оказывает класс Java java.util.Date. В результате он наследует некоторые нежелательные черты, такие как характеристики изменяемых данных и месяц, начинающийся с 0.

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

Создание объекта даты с пользовательским вводом

Вы также можете использовать конструктор вместе со строковыми данными. Если вы передаете строку при создании Date, он внутренне вызывает Date.parse() и вычисляет правильное значение. Эта функция поддерживает спецификации RFC2888 и ISO-8601. Однако, как описано в документе MDN Date.parse(), возвращаемое значение этого метода варьируется от браузера к браузеру, и формат типа строки может повлиять на предсказание точного значения. Таким образом, рекомендуется не использовать этот метод.

Например, строка вида 2015-10-12 12:00:00 возвращает NaN в Safari и Internet Explorer, в то же время эта строка возвращает местный часовой пояс в Chrome и Firefox. В некоторых случаях она возвращает значение, основанное на стандарте UTC.

Создание объекта даты с использованием данных сервера

Предположим теперь, что вы собираетесь получать данные с сервера. Если данные имеют числовое значение времени Unix, вы можете просто использовать конструктор для создания объекта Date. Когда конструктор Date получает единственный числовой параметр, он распознается, как значение времени Unix в миллисекундах. (Внимание: JavaScript обрабатывает время Unix в миллисекундах. Это означает, что стандартное числовое значение времени Unix необходимо умножить на 1000) Результат выполнения следующего кода будет таким же, как и в предыдущем примере.

Что если вместо времени Unix использовать строковый тип, такой как ISO-8601? Как я объяснил в предыдущем абзаце, метод Date.parse() ненадежен, и его лучше не использовать. Однако, поскольку в ECMAScript 5 и более поздних версиях указана поддержка ISO-8601, вы можете использовать строки в формате, указанном в ISO-8601, для конструктора Date в Internet Explorer 9.0 или более поздней версии, который поддерживает ECMAScript 5 при осторожном использовании.

Если вы поддерживаете старые браузеры, не забудьте добавить букву Z в конце. Без неё старые браузеры иногда интерпретируют строку на основе вашего местного времени, а не UTC. Ниже приведен пример запуска в Internet Explorer 10.

Создание данных для передачи на сервер

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

Если это время Unix, вы можете просто использовать метод getTime() для выполнения этого. (Обратите внимание на использование миллисекунд.)

Вы также можете использовать метод toGMTString() или toUTCString() для создания строк в формате UTC. Поскольку они возвращают строку, удовлетворяющую стандарту RFC-1123, вы можете использовать это по мере необходимости.

Изменение местного часового пояса

Проблема преобразования местного часового пояса

Если вы продолжите работать с приведенным выше примером еще немного, вы скоро столкнетесь с проблемой. Пользователь хочет проверить время по местному времени Нью-Йорка, а затем изменить дату с 10-го на 15-е. Если вы используете метод setDate() объекта Date, вы можете изменить дату, не изменяя другие значения.

Некоторые из вас могут задаться вопросом, почему я добавил использование преобразования данных, когда мне все равно нужно преобразовать их обратно перед возвратом. Выглядит так, будто я могу просто обработать их без преобразования и временно создать преобразованный объект Date только при форматировании. Однако не все так просто. Если вы измените дату объекта Date по сеульскому времени с 11-го на 15-е, добавляются 4 дня (24 * 4 * 60 * 60 * 1000). Однако по местному времени Нью-Йорка, поскольку дата была изменена с 10-го на 15-е, в результате было добавлено 5 дней (24 * 5 * 60 * 60 * 1000). Это означает, что для получения корректного результата вы должны рассчитывать даты на основе местного смещения часового пояса относительно часового пояса UTC.

Напротив, если местным часовым поясом пользователя является Нью-Йорк и он хочет узнать время в Сеуле, нет необходимости применять летнее время, что вызывает другую проблему.

Проще говоря, вы не можете использовать только полученное смещение для выполнения точных операций в зависимости от выбранного вами часового пояса. Если вы вспомните то, что мы обсуждали в предыдущей части этого документа, вы поймете, что в этом преобразовании все еще есть дыра, при условии что вы помните правила летнего времени. Чтобы получить точное значение, вам нужна база данных, содержащая всю историю изменений смещения часового пояса, например База данных часовых поясов IANA.

Чтобы решить эту проблему, необходимо сохранить всю базу данных часовых поясов и всякий раз, когда данные о дате или времени извлекаются из объекта Date, найти дату и соответствующее смещение часового пояса, а затем преобразовать значение, используя описанный выше процесс. Теоретически это возможно. Но на самом деле это требует слишком больших усилий, и проверка целостности преобразованных данных также будет сложной задачей. Но пока не расстраивайтесь. До сих пор мы обсуждали некоторые проблемы JavaScript и способы их решения. Теперь мы готовы использовать хорошо созданную библиотеку.

Moment timezone

В этой статье я не буду подробно обсуждать, как использовать библиотеку или структуру библиотеки. Я просто покажу вам, насколько просто решить проблемы, которые я обсуждал ранее. Если кому-то интересно, см. Документацию Moment Timezone.

Давайте решим проблему, показанную на картинке, с помощью Moment Timezone.

Заключение

До сих пор мы обсуждали API-интерфейсы часовых поясов, поддерживаемые JavaScript, и их проблемы. Если вам не нужно вручную изменять местный часовой пояс, вы можете реализовать необходимый функционал даже с помощью базовых API-интерфейсов при условии, что вы используете Internet Explorer 9 или выше. Однако, если вам нужно вручную изменить местный часовой пояс, все становится очень сложным. В регионе, где нет летнего времени и политика часовых поясов практически не меняется, вы можете частично реализовать ее, используя getTimezoneOffset() для преобразования данных. Но если вам нужна полная поддержка часовых поясов, не создавайте ее с нуля. Лучше используйте такую библиотеку, как Moment Timezone.

Заключение от переводчика

Очевидно, здесь повествование обрывается, и хотелось бы углубиться в особенности различных библиотек для работы с датами в JS, в частности в Moment.JS, Luxon и Date FNS. Возможно в дальнейшем здесь появится ссылка на статью посвященную этим библиотекам.

Благодарю за помощь в подготовке перевода Степана Омельченко и Марию Пилипончик. Это было не просто, но мы смогли!

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *