Spec-Zone.ru › Moment.js Timezone

Часовые пояса Moment.js

Где использовать

Для использования moment-timezone вам понадобятся moment@2.9.0+, moment-timezone.js и данные moment-timezone.

Для удобства на сайте momentjs.com/timezone/ доступны сборки со всеми данными о часовых поясах или с их подмножеством.

  • moment-timezone-with-data.js рекомендуется для серверных сред (Node.js) и охватывает все доступные годы.
  • moment-timezone-with-data-10-year-range.js рекомендуется для большинства браузерных сред и охватывает период +/- 5 лет от года публикации.
  • moment-timezone-with-data-1970-2030.js охватывает диапазон в 60 лет и подходит тем, кому нужно больше данных, но не нужен увеличенный размер полного файла с данными.

Если вы используете один из файлов выше, вам всё равно понадобится moment.js, но moment-timezone.js не нужен, так как он включён в файл.

Node.js

npm install moment-timezone

В Node.js все данные загружаются заранее. Дополнительный код для загрузки данных не требуется.

var moment = require('moment-timezone');
moment().tz("America/Los_Angeles").format();

В формате нативных модулей ECMAScript (или в TypeScript):

import moment from 'moment-timezone';
moment().tz("America/Los_Angeles").format();

Примечание: Не нужно также подключать с помощью require/import базовую библиотеку moment. Moment Timezone автоматически загрузит модуль moment, расширит его и вернёт изменённый экземпляр.

Менеджеры пакетов, такие как npm и yarn, иногда могут приводить к установке нескольких версий moment. Импорт только из moment-timezone может помочь обеспечить единообразное использование одной и той же версии. Более подробное объяснение, включая инструкции по устранению возможных проблем с версиями, приведено в этом комментарии к issue #982.

// Unnecessary, can cause issues with package managers
import moment from 'moment';
import 'moment-timezone';

// Correct
import moment from 'moment-timezone';

Готовые сборки также включены в пакет npm и могут загружаться напрямую. Они позволяют импортировать библиотеку с меньшим подмножеством данных.

import moment from 'moment-timezone/builds/moment-timezone-with-data-10-year-range.js'; // or .min.js

Также можно импортировать только библиотеку без предварительно загруженных данных.

import moment from 'moment-timezone/moment-timezone.js'; // or .min.js
moment.tz.load(customData);

Браузер

<script src="moment.js"></script>
<script src="moment-timezone-with-data.js"></script>

При использовании Moment Timezone в браузере нужно загрузить как библиотеку, так и данные.

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

moment().tz("America/Los_Angeles").format();

Require.js

require.config({
    paths: {
        "moment": "path/to/moment"
    }
});
define(["path/to/moment-timezone-with-data"], function (moment) {
    moment().tz("America/Los_Angeles").format();
});

Webpack

npm install moment-timezone
var moment = require('moment-timezone');
moment().tz("America/Los_Angeles").format();

Примечание: По умолчанию webpack включает в сборку все данные moment-timezone (в moment-timezone 0.5.25 это более 900 КБ в минифицированном виде). Чтобы исключить ненужные данные и включить в сборку только необходимые данные о часовых поясах и диапазонах дат, добавьте пакет moment-timezone-data-webpack-plugin:

// webpack.config.js
const MomentTimezoneDataPlugin = require('moment-timezone-data-webpack-plugin');
const currentYear = new Date().getFullYear();

module.exports = {
    plugins: [
        // To include only specific zones, use the matchZones option
        new MomentTimezoneDataPlugin({
            matchZones: /^America/
        }),

        // To keep all zones but limit data to specific years, use the year range options
        new MomentTimezoneDataPlugin({
            startYear: currentYear - 5,
            endYear: currentYear + 5,
        }),
    ],
};

Также можно использовать готовые сборки, включённые в пакет npm, и загружать их напрямую. Подробнее см. в разделе о Node.js.

Пример сокращения данных локалей Moment, включённых в сборку, см. также в основной документации Moment.js по Webpack. Вместе эти методы могут значительно уменьшить размер итоговой сборки (более чем на 1 МБ в минифицированном виде или на 85 КБ в минифицированном виде с gzip-сжатием).

Использование часовых поясов

Moment.js предоставляет два интерфейса для работы с часовыми поясами.

moment.tz(..., String) выполняет разбор в заданном часовом поясе

Он принимает те же аргументы, что и конструктор moment, но использует последний аргумент как идентификатор часового пояса:

var a = moment.tz("2013-11-18 11:55", "Asia/Taipei");
var b = moment.tz("2013-11-18 11:55", "America/Toronto");

a.format(); // 2013-11-18T11:55:00+08:00
b.format(); // 2013-11-18T11:55:00-05:00

a.utc().format(); // 2013-11-18T03:55Z
b.utc().format(); // 2013-11-18T16:55Z

Обратите внимание, что у созданных объектов moment время UTC различается, поскольку они были созданы в разных часовых поясах.

moment().tz(String) выполняет преобразование в указанный часовой пояс

var a = moment.utc("2013-11-18 11:55").tz("Asia/Taipei");
var b = moment.utc("2013-11-18 11:55").tz("America/Toronto");

a.format(); // 2013-11-18T19:55:00+08:00
b.format(); // 2013-11-18T06:55:00-05:00

a.utc().format(); // 2013-11-18T11:55Z
b.utc().format(); // 2013-11-18T11:55Z

В этом примере сначала создаётся объект moment.utc("2013-11-18 11:55") в UTC, а затем его часовой пояс меняется на указанный. Это также работает, если создать объект в часовом поясе по умолчанию: moment("2013-11-18 11:55").

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

Разбор в часовом поясе

moment.tz(..., String);

Конструктор moment.tz принимает те же аргументы, что и конструктор moment, но использует последний аргумент как идентификатор часового пояса.

var a = moment.tz("2013-11-18 11:55", "America/Toronto");
var b = moment.tz("May 12th 2014 8PM", "MMM Do YYYY hA", "America/Toronto");
var c = moment.tz(1403454068850, "America/Toronto");
a.format(); // 2013-11-18T11:55:00-05:00
b.format(); // 2014-05-12T20:00:00-04:00
c.format(); // 2014-06-22T12:21:08-04:00

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

moment.tz("2013-12-01", "America/Los_Angeles").format(); // 2013-12-01T00:00:00-08:00
moment.tz("2013-06-01", "America/Los_Angeles").format(); // 2013-06-01T00:00:00-07:00

Смещение учитывается только при создании объекта из массива, строки без смещения или объекта.

var arr = [2013, 5, 1],
    str = "2013-12-01",
    obj = { year : 2013, month : 5, day : 1 };

moment.tz(arr, "America/Los_Angeles").format(); // 2013-06-01T00:00:00-07:00
moment.tz(str, "America/Los_Angeles").format(); // 2013-12-01T00:00:00-08:00
moment.tz(obj, "America/Los_Angeles").format(); // 2013-06-01T00:00:00-07:00

moment.tz(arr, "America/New_York").format();    // 2013-06-01T00:00:00-04:00
moment.tz(str, "America/New_York").format();    // 2013-12-01T00:00:00-05:00
moment.tz(obj, "America/New_York").format();    // 2013-06-01T00:00:00-04:00

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

var zone = "America/Los_Angeles";
moment.tz('2013-06-01T00:00:00',       zone).format(); // 2013-06-01T00:00:00-07:00
moment.tz('2013-06-01T00:00:00-04:00', zone).format(); // 2013-05-31T21:00:00-07:00
moment.tz('2013-06-01T00:00:00+00:00', zone).format(); // 2013-05-31T17:00:00-07:00

Метки времени Unix и объекты Date указывают на конкретные моменты времени, поэтому учитывать смещение часового пояса при создании объекта не имеет смысла. Использование moment.tz(Number|Date, zone) функционально эквивалентно использованию moment(Number|Date).tz(zone).

var timestamp = 1403454068850,
    date = new Date(timestamp);

moment.tz(timestamp, "America/Los_Angeles").format(); // 2014-06-22T09:21:08-07:00
moment(timestamp).tz("America/Los_Angeles").format(); // 2014-06-22T09:21:08-07:00

moment.tz(date, "America/Los_Angeles").format();      // 2014-06-22T09:21:08-07:00
moment(date).tz("America/Los_Angeles").format();      // 2014-06-22T09:21:08-07:00

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

moment.tz('It is 2012-05-25', 'YYYY-MM-DD', "America/Toronto").isValid();       // true 
moment.tz('It is 2012-05-25', 'YYYY-MM-DD', true, "America/Toronto").isValid(); // false
moment.tz('2012-05-25', 'YYYY-MM-DD', true, "America/Toronto").isValid();       // true
moment.tz('2012-05.25', 'YYYY-MM-DD', true, "America/Toronto").isValid();       // false

Неоднозначности при разборе

Из-за перехода на летнее время возможны ситуации, когда заданного времени не существует или оно встречается дважды.

Перевод часов вперёд весной

Весной, в начале периода летнего времени, часы переводят на час вперёд. В действительности движется не время, а смещение.

Из-за увеличения смещения создаётся иллюзия, что один час исчез. По мере хода часов можно увидеть, как время перескакивает с 1:58 на 1:59, а затем на 3:00. С указанием смещения легче понять, что происходит на самом деле.

1:58 -5
1:59 -5
3:00 -4
3:01 -4

В результате время между 1:59:59 и 3:00:00 никогда не наступает. Moment Timezone учитывает это. При попытке разобрать несуществующее время оно будет сдвинуто вперёд на величину разрыва, вызванного переходом на летнее время (обычно на 1 час).

moment.tz("2012-03-11 01:59:59", "America/New_York").format() // 2012-03-11T01:59:59-05:00
moment.tz("2012-03-11 02:00:00", "America/New_York").format() // 2012-03-11T03:00:00-04:00
moment.tz("2012-03-11 02:59:59", "America/New_York").format() // 2012-03-11T03:59:59-04:00
moment.tz("2012-03-11 03:00:00", "America/New_York").format() // 2012-03-11T03:00:00-04:00

В этом примере часа с двух до трёх не существует, поэтому он считается эквивалентным часу с трёх до четырёх.

Перевод часов назад осенью

Осенью, в конце периода летнего времени, часы переводят на час назад. И снова время не движется назад — меняется только смещение. В этом случае создаётся иллюзия, что один час повторяется.

Здесь также легче понять, что происходит на самом деле, если указать смещение.

1:58 -4
1:59 -4
1:00 -5
1:01 -5

Moment Timezone разрешает эту неоднозначность, всегда используя более ранний вариант повторяющегося часа.

moment.tz("2012-11-04 00:59:59", "America/New_York"); // 2012-11-04T00:59:59-04:00
moment.tz("2012-11-04 01:00:00", "America/New_York"); // 2012-11-04T01:00:00-04:00
moment.tz("2012-11-04 01:59:59", "America/New_York"); // 2012-11-04T01:59:59-04:00
moment.tz("2012-11-04 02:00:00", "America/New_York"); // 2012-11-04T02:00:00-05:00

Создать объект moment для более позднего варианта повторяющегося часа не получится, если при разборе не указать смещение.

moment.tz("2012-11-04 01:00:00-04:00", "America/New_York"); // 2012-11-04T01:00:00-04:00
moment.tz("2012-11-04 01:00:00-05:00", "America/New_York"); // 2012-11-04T01:00:00-05:00

Преобразование в часовой пояс

moment().tz(String);
moment().tz(String, Boolean);

Метод-мутатор moment#tz изменяет часовой пояс и обновляет смещение.

moment("2013-11-18").tz("America/Toronto").format('Z'); // -05:00
moment("2013-11-18").tz("Europe/Berlin").format('Z');   // +01:00

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

var m = moment.tz("2013-11-18 11:55", "America/Toronto");
m.format();                     // 2013-11-18T11:55:00-05:00
m.startOf("day").format();      // 2013-11-18T00:00:00-05:00
m.tz("Europe/Berlin").format(); // 2013-11-18T06:00:00+01:00
m.startOf("day").format();      // 2013-11-18T00:00:00+01:00

Без аргументов moment#tz возвращает:

  • название часового пояса, назначенного экземпляру moment, или
  • undefined, если часовой пояс не задан.
var m = moment.tz("2013-11-18 11:55", "America/Toronto");
m.tz();  // America/Toronto
var m = moment.tz("2013-11-18 11:55");
m.tz() === undefined;  // true

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

var m = moment.tz("2013-11-18 11:55", "America/Toronto");
m.format();                           // 2013-11-18T11:55:00-05:00
m.tz('Europe/Berlin', true).format()  // 2013-11-18T11:55:00+01:00

Дополнения к форматированию

moment.tz(String).format("Z z"); // -08:00 PST
moment.tz(String).zoneAbbr();    // PST
moment.tz(String).zoneName();    // PST

Помимо форматирования информации +00:00, Moment Timezone также предоставляет сокращённое название часового пояса.

moment.tz([2012, 0], 'America/New_York').format('z');    // EST
moment.tz([2012, 5], 'America/New_York').format('z');    // EDT
moment.tz([2012, 0], 'America/Los_Angeles').format('z'); // PST
moment.tz([2012, 5], 'America/Los_Angeles').format('z'); // PDT

Обратите внимание, что эти сокращения могут меняться в зависимости от смещения часового пояса. Это позволяет различать смещения в местах, где используется или не используется летнее время.

// Denver observes DST
moment.tz([2012, 0], 'America/Denver').format('Z z');  // -07:00 MST
moment.tz([2012, 5], 'America/Denver').format('Z z');  // -06:00 MDT
// Phoenix does not observe DST
moment.tz([2012, 0], 'America/Phoenix').format('Z z'); // -07:00 MST
moment.tz([2012, 5], 'America/Phoenix').format('Z z'); // -07:00 MST

Также обратите внимание, что эти сокращения не являются уникальными во всём мире. Ниже видно, что для центрального стандартного времени США и стандартного времени Китая используется одно и то же сокращение.

moment.tz('2016-01-01', 'America/Chicago').format('z');    // CST
moment.tz('2016-01-01', 'Asia/Shanghai').format('z');      // CST

Для получения сокращения часового пояса также можно использовать moment#zoneAbbr. Этот метод использует moment.js при форматировании токена z.

moment.tz([2012, 0], 'America/New_York').zoneAbbr(); // EST
moment.tz([2012, 5], 'America/New_York').zoneAbbr(); // EDT

Moment.js также предоставляет точку расширения для полного названия часового пояса. Поскольку такие строки обычно локализуются, Moment Timezone не задаёт полные названия часовых поясов.

Чтобы задать полные названия, можно переопределить moment.fn.zoneName и использовать токен zz.

var abbrs = {
    EST : 'Eastern Standard Time',
    EDT : 'Eastern Daylight Time',
    CST : 'Central Standard Time',
    CDT : 'Central Daylight Time',
    MST : 'Mountain Standard Time',
    MDT : 'Mountain Daylight Time',
    PST : 'Pacific Standard Time',
    PDT : 'Pacific Daylight Time',
};

moment.fn.zoneName = function () {
    var abbr = this.zoneAbbr();
    return abbrs[abbr] || abbr;
};

moment.tz([2012, 0], 'America/New_York').format('zz');    // Eastern Standard Time
moment.tz([2012, 5], 'America/New_York').format('zz');    // Eastern Daylight Time
moment.tz([2012, 0], 'America/Los_Angeles').format('zz'); // Pacific Standard Time
moment.tz([2012, 5], 'America/Los_Angeles').format('zz'); // Pacific Daylight Time

Обратите внимание, что токен форматирования z не всегда отображает сокращённое название часового пояса, а вместо этого показывает смещения времени для каждого региона.

moment.tz('America/Los_Angeles').format('z')  // "PDT"     (abbreviation)
moment.tz('Asia/Magadan').format('z')         // "+11"     (3-char offset)
moment.tz('Asia/Colombo').format('z')         // "+0530"   (5-char offset)

Часовой пояс по умолчанию

moment.tz.setDefault(String);

По умолчанию объекты moment создаются в местном часовом поясе. Местный часовой пояс определяется средой JS, например браузером или сервером, таким как Node.js.

Чтобы изменить часовой пояс по умолчанию, используйте moment.tz.setDefault с допустимым часовым поясом.

moment.tz.setDefault("America/New_York");

Чтобы сбросить часовой пояс по умолчанию до местного, вызовите moment.tz.setDefault без аргументов.

moment.tz.setDefault();

Это глобальная настройка, общая для всех модулей.

Последующие вызовы moment.tz.setDefault не влияют на уже существующие объекты moment и их копии.

Определение часового пояса пользователя

moment.tz.guess();
moment.tz.guess(Boolean);

В поддерживаемых браузерах Moment Timezone использует API интернационализации (Intl.DateTimeFormat().resolvedOptions().timeZone), чтобы определить часовой пояс пользователя.

В других браузерах определить часовой пояс непросто, поскольку они предоставляют мало информации. В таких случаях Moment Timezone использует Date#getTimezoneOffset и Date#toString для нескольких моментов времени вблизи текущего года, чтобы собрать как можно больше информации о среде браузера. Затем эта информация сравнивается со всеми загруженными данными о часовых поясах, и возвращается наиболее подходящий вариант. Если несколько вариантов одинаково подходят, возвращается часовой пояс города с наибольшим населением.

По умолчанию Moment Timezone кэширует определённый часовой пояс. Это означает, что последующие вызовы moment.tz.guess() всегда будут возвращать одно и то же значение.

Можно вызвать moment.tz.guess() с необязательным логическим аргументом "ignoreCache". Если задать значение true, кэш будет проигнорирован и заменён новым значением.

moment.tz.guess(); // America/Chicago
// suppose the client's timezone changes to Europe/Berlin
moment.tz.guess(); // America/Chicago
moment.tz.guess(true); // Europe/Berlin
moment.tz.guess(); // Europe/Berlin

Получение всех часовых поясов

moment.tz.names(); // String[]

Чтобы получить список всех доступных названий часовых поясов, используйте moment.tz.names.

moment.tz.names(); // ["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", ...]

Получение часовых поясов страны

moment.tz.zonesForCountry(String); // String[]
moment.tz.zonesForCountry(String, Boolean);

Чтобы получить список часовых поясов определённой страны, используйте moment.tz.zonesForCountry(). Метод принимает двухбуквенный код страны по стандарту ISO 3166-1.

moment.tz.zonesForCountry('US');

По умолчанию этот метод возвращает названия часовых поясов, отсортированные по алфавиту:

["America/Adak", "America/Anchorage", ... "Pacific/Honolulu"]

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

moment.tz.zonesForCountry('CN', true);

В этом случае возвращается массив объектов с названием часового пояса и его смещением на текущую дату и время.

[
   { name: "Asia/Shanghai", offset: -480 },
   { name: "Asia/Urumqi", offset: -360 }
]

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

Получить все известные коды стран, содержащиеся в данных, можно с помощью метода moment.tz.countries().

Примечание: Иногда эти методы могут возвращать неожиданные данные. Подробнее см. в руководстве «Страны и часовые пояса».

Объект часового пояса

Чтобы сопоставить метку времени со смещением, Moment Timezone использует объект Zone.

Хотя вам, скорее всего, не придётся использовать его напрямую, конструктор этого объекта доступен в пространстве имён moment.tz.Zone.

У этого объекта есть 5 свойств.

{
    name       : 'America/Los_Angeles',          // the unique identifier
    abbrs      : ['PDT', 'PST'],                 // the abbreviations
    untils     : [1414918800000, 1425808800000], // the timestamps in milliseconds
    offsets    : [420, 480],                     // the offsets in minutes
    population : 15000000                        // a rough population count for the largest city in this zone
}

Название

zone.name; // America/Los_Angeles

Уникальное название часового пояса. Подробнее о соглашении об именовании см. в рекомендациях по именованию базы часовых поясов IANA.

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

Не предполагается, что неопытные пользователи смогут самостоятельно выбрать название. Распространители должны предоставлять документацию и/или простой интерфейс выбора, объясняющий каждое название с помощью карты или описательного текста, например «Чешская Республика», а не названия часового пояса "Europe/Prague".

Предоставление полного списка переведённых названий часовых поясов для каждой локали выходит за рамки Moment Timezone. Для этой цели в проекте Unicode CLDR представлены сопоставления с учётом локали.

Сокращение

zone.abbr(timestamp); // PST

Получить сокращение для заданной метки времени (в миллисекундах) из Zone.

moment.tz.zone('America/Los_Angeles').abbr(1403465838805); // PDT
moment.tz.zone('America/Los_Angeles').abbr(1388563200000); // PST

Смещение

zone.utcOffset(timestamp); // 480

Получить смещение для заданной метки времени (в миллисекундах) из Zone.

moment.tz.zone('America/Los_Angeles').utcOffset(1403465838805); // 420
moment.tz.zone('America/Los_Angeles').utcOffset(1388563200000); // 480

Для совместимости с POSIX смещения инвертированы. Поэтому Etc/GMT-X будет иметь смещение +X, а Etc/GMT+X — -X. Это обусловлено базой часовых поясов IANA, а не произвольным решением Moment.js. Поэтому предпочтительнее использовать идентификаторы, основанные на местоположении, а не идентификаторы с фиксированным смещением.

Это также описано в статье Википедии о базе данных:

Специальная область «Etc» используется для некоторых административных часовых поясов, в частности для «Etc/UTC», обозначающего координированное всемирное время. Для соответствия стилю POSIX знак в названиях часовых поясов, начинающихся с «Etc/GMT», противоположен стандартному соглашению ISO 8601. В области «Etc» для часовых поясов к западу от GMT используется положительный знак, а к востоку — отрицательный (например, «Etc/GMT-14» означает, что время опережает GMT на 14 часов).

Например, идентификатор Europe/Madrid даёт результат, отличный от Etc/GMT+1.

moment().tz('Etc/GMT+1').format('YYYY-MM-DD HH:mm ZZ');
// '2014-12-18 11:22 -0100'
moment().tz('Europe/Madrid').format('YYYY-MM-DD HH:mm ZZ');
// '2014-12-18 13:22 +0100'

Разбор смещения

zone.parse(timestamp); // 480

Разобрать смещение для метки времени, созданной из Date.UTC в этом часовом поясе.

Именно этот метод Moment Timezone использует для разбора входных данных с учётом часового пояса. В общих чертах процесс выглядит следующим образом.

Предположим, что нужно найти точный момент времени March 19 2014 8:30 am в Нью-Йорке. Поскольку смещение в Нью-Йорке меняется между -04:00 и -05:00, нам неизвестно, каким оно было 19 марта.

Вместо этого мы создаём метку времени в UTC и передаём её в zone.parse, который возвращает смещение на тот момент.

var zone = moment.tz.zone('America/New_York');
zone.parse(Date.UTC(2012, 2, 19, 8, 30)); // 240

Этот код обрабатывает случаи, описанные выше в разделе «Неоднозначности при разборе».

var zone = moment.tz.zone('America/New_York');
zone.parse(Date.UTC(2012, 2, 11, 1, 59)); // 300
zone.parse(Date.UTC(2012, 2, 11, 2, 0)); // 240

Форматы данных

Moment Timezone использует два формата данных: распакованный — для вычислений, и упакованный — для передачи в минифицированном виде.

Распакованный формат

Распакованный формат в точности соответствует объекту часового пояса.

Данные ниже относятся к Лос-Анджелесу за период с 2014 по 2018 год.

{
    name       : 'America/Los_Angeles',
    abbrs      : ['PST', 'PDT','PST', 'PDT', 'PST', 'PDT', 'PST', 'PDT', 'PST', 'PDT', 'PST'],
    untils     : [1394359200000, 1414918800000, 1425808800000, 1446368400000, 1457863200000, 1478422800000, 1489312800000, 1509872400000, 1520762400000, 1541322000000, null],
    offsets    : [480, 420, 480, 420, 480, 420, 480, 420, 480, 420, 480],
    population : 15000000,
    countries  : ['US']
}

Длины abbrs, untils, offsets одинаковы. Значения offset и abbr с любым индексом действуют только до тех пор, пока метка времени меньше значения until с тем же индексом.

Удобно читать это так: «между untils[n-1] и untils[n] сокращение должно быть abbrs[n], а смещение — offsets[n]».

Обратите внимание, что значения untils измеряются в миллисекундах, а значения offsets — в минутах.

Упакованный формат

В упакованном формате данные о часовом поясе представлены одной строкой.

Данные ниже относятся к Лос-Анджелесу за период с 2014 по 2018 год. Другие часовые пояса можно найти в упакованном исходном файле.

'America/Los_Angeles|PST PDT|80 70|01010101010|1Lzm0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0|15e6'

Чтобы максимально сократить размер, мы использовали очень компактный формат хранения данных.

Данные разделены на 6 секций с помощью символов вертикальной черты.

№ Тип Пример
0 Название America/Los_Angeles
1 Карта сокращений PST PDT
2 Карта смещений 80 70
3 Индекс сокращений/смещений 01010101010
4 Разница меток времени 1Lzm0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0
5 Население 15e6

Название: Каноническое название часового пояса.

Карта сокращений: Разделённый пробелами список всех сокращений, когда-либо использовавшихся в этом часовом поясе.

Карта смещений: Разделённый пробелами список всех смещений, когда-либо использовавшихся в этом часовом поясе. Смещения указаны в минутах в системе счисления с основанием 60.

Индекс сокращений/смещений: Плотно упакованный массив индексов в карты смещений и сокращений. Значения также представлены в системе счисления с основанием 60.

Разницы меток времени: Здесь хранятся метки времени.

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

Первый элемент массива — метка времени Unix в минутах. Все элементы после первого — количество минут, которое нужно прибавить к предыдущему значению при распаковке. Все значения хранятся в системе счисления с основанием 60.

Как видно из примера выше, разницы меток времени часто повторяются из года в год. Благодаря этим повторениям gzip может сжать данные ещё эффективнее, чем при использовании полных меток времени.

Население: Приблизительная численность населения города, в честь которого назван часовой пояс.

Значение представлено не в системе счисления с основанием 60, а в научной экспоненциальной нотации. Например, значение 15e6 означает 15 * 106 (то есть 15 и 6 нулей после него), а значит, представляет число 15,000,000.

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

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

Система счисления с основанием 60?

Возможно, вы задаётесь вопросом, почему используется система счисления с основанием 60. Система с основанием 62 часто применяется для сжатия ASCII-данных: a-z используется для представления 10-35, а A-Z — для представления 36-61.

Система с основанием 62 могла бы сэкономить несколько байтов, но многие данные Moment Timezone удобно представлять числами, кратными 60.

В часе 60 минут, а в минуте 60 секунд. 3 часа — это 30 минут в системе счисления с основанием 60 и 300 секунд в системе счисления с основанием 60, а не 180 и 10800 в системе с основанием 10 или 2U и 2Oc в системе с основанием 62.

Формат ссылок

Чтобы избежать дублирования, упаковщик данных Moment Timezone создаёт ссылки для двух часовых поясов с полностью совпадающими данными.

Эти данные состоят из двух названий часовых поясов, разделённых символом вертикальной черты.

moment.tz.add('America/Los_Angeles|PST PDT|80 70|01010101010|1Lzm0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0');
moment.tz.link('America/Los_Angeles|US/Pacific');
moment.tz("2013-12-01", "America/Los_Angeles").format(); // 2013-12-01T00:00:00-08:00
moment.tz("2013-12-01", "US/Pacific").format();          // 2013-12-01T00:00:00-08:00

Загрузка данных

После упаковки и передачи данных клиенту их нужно добавить в Moment Timezone.

В Node.js и готовых сборках это происходит автоматически. Если используется другой способ загрузки, возможно, данные придётся загрузить самостоятельно.

Добавление часового пояса

moment.tz.add(PackedZoneString)
moment.tz.add(PackedZoneString[])

Чтобы добавить данные о часовом поясе в Moment Timezone, используйте moment.tz.add.

moment.tz.add('America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0');

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

moment.tz.add([
    'America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0',
    'America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0'
]);

Примечание: приведённые выше данные часового пояса являются примером и устарели. Актуальные данные см. в исходных данных moment-timezone.

Добавление ссылки

moment.tz.link(PackedLinkString)
moment.tz.link(PackedLinkString[])

Чтобы связать два названия часовых поясов с одними и теми же данными, используйте moment.tz.link.

Передаваемые строки должны иметь формат ссылок: два названия часовых поясов, разделённые символом вертикальной черты.

moment.tz.link('America/Los_Angeles|US/Pacific');

Чтобы добавить несколько ссылок одновременно, передайте массив строк ссылок.

moment.tz.link([
    'America/Los_Angeles|US/Pacific',
    'America/New_York|US/Eastern'
]);

Загрузка пакета данных

moment.tz.load({
    zones : [],
    links : [],
    version : '2014e'
});

Данные Moment Timezone берутся из базы часовых поясов IANA. Новые версии выпускаются периодически по мере изменения законов о часовых поясах в разных странах.

Версии называются по году и следующей за ним букве. 2014a 2014b 2014c...

Для согласованного управления версиями Moment Timezone также использует формат объединённого объекта.

{
    version : '2014e',
    zones : [
        'America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0',
        'America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0'
    ],
    links : [
        'America/Los_Angeles|US/Pacific',
        'America/New_York|US/Eastern'
    ]
}

Чтобы загрузить пакет в Moment Timezone, используйте moment.tz.load.

moment.tz.load({
    version : '2014e',
    zones : [...],
    links : [...]
})

Проверка наличия часового пояса

moment.tz.zone(name); // Zone or null

Чтобы проверить наличие часового пояса, используйте moment.tz.zone. Метод вернёт объект Zone, если он был загружен, и null, если он не был загружен.

moment.tz.zone("UnloadedZone"); // null
moment.tz.add("UnloadedZone|UZ|0|0|");
moment.tz.zone("UnloadedZone"); // Zone { name : "UnloadedZone", ...}

Получение названий часовых поясов

moment.tz.names(); // String[]

Чтобы получить список всех доступных названий часовых поясов, используйте moment.tz.names.

moment.tz.names(); // ["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", ...]

Утилиты для работы с данными

Из-за сложности упакованного и распакованного форматов данных в Moment Timezone предусмотрен ряд тщательно протестированных вспомогательных функций для работы с данными.

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

Методы упаковки данных и создания их подмножеств находятся в дополнительном файле moment-timezone-utils.js. Этот файл добавляет несколько методов в пространство имён moment.tz.

// in moment-timezone.js
moment.tz.unpack
moment.tz.unpackBase60
// in moment-timezone-utils.js
moment.tz.pack
moment.tz.packBase60
moment.tz.createLinks
moment.tz.filterYears
moment.tz.filterLinkPack

Упаковка

moment.tz.pack(UnpackedObject); // PackedString

Преобразует данные из распакованного формата в упакованный формат.

var unpacked = {
    name       : 'Indian/Mauritius',
    abbrs      : ['LMT', 'MUT', 'MUST', 'MUT', 'MUST', 'MUT'],
    offsets    : [-230, -240, -300, -240, -300, -240],
    untils     : [-1988164200000, 403041600000, 417034800000, 1224972000000, 1238274000000, null],
    population : 150000
};
moment.tz.pack(unpacked); // "Indian/Mauritius|LMT MUT MUST|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0|15e4"

Распаковка

moment.tz.unpack(PackedString); // UnpackedObject

Преобразует данные из упакованного формата в распакованный формат.

var packed = "Indian/Mauritius|LMT MUT MUST|-3O -40 -50|012121|-2xorO 34unO 14L0 12kr0 11z0|15e4";

moment.tz.unpack(packed);
// {
//     name       : 'Indian/Mauritius',
//     abbrs      : ['LMT', 'MUT', 'MUST', 'MUT', 'MUST', 'MUT'],
//     offsets    : [-230, -240, -300, -240, -300, -240],
//     untils     : [-1988164200000, 403041600000, 417034800000, 1224972000000, 1238274000000, null],
//     population : 150000
// };

Упаковка в систему счисления с основанием 60

moment.tz.packBase60(Number); // Base60String

Преобразовать число из системы счисления с основанием 10 в строку в системе с основанием 60.

moment.tz.packBase60(9);    // 9
moment.tz.packBase60(10);   // a
moment.tz.packBase60(59);   // X
moment.tz.packBase60(1337); // mh

Как и Number.prototype.toFixed, moment.tz.packBase60 принимает второй аргумент, задающий количество знаков точности.

moment.tz.packBase60(1.1667,   1); // 1.a
moment.tz.packBase60(20.12345, 3); // k.7op
moment.tz.packBase60(59,       1); // X

Одиночный 0 перед десятичной точкой опускается.

moment.tz.packBase60(1.1667, 1); // 1.a
moment.tz.packBase60(0.1667, 1); // .a

Нули в конце дробной части опускаются.

moment.tz.packBase60(1/6, 1); // .a
moment.tz.packBase60(1/6, 5); // .a
moment.tz.packBase60(59, 5);  // X

Распаковка из системы счисления с основанием 60

moment.tz.unpackBase60(Base60String); // Number

Преобразовать строку в системе счисления с основанием 60 в число в системе с основанием 10.

moment.tz.unpackBase60('9');     // 9
moment.tz.unpackBase60('a');     // 10
moment.tz.unpackBase60('X');     // 59
moment.tz.unpackBase60('mh');    // 1337
moment.tz.unpackBase60('1.9');   // 1.15
moment.tz.unpackBase60('k.7op'); // 20.123449074074074

Создание ссылок

moment.tz.createLinks(UnpackedBundle); // UnpackedBundle

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

var unlinked = {
    zones : [
        {name:"Zone/One",abbrs:["OST","ODT"],offsets:[60,120],untils:[403041600000,417034800000]},
        {name:"Zone/Two",abbrs:["OST","ODT"],offsets:[60,120],untils:[403041600000,417034800000]}
    ],
    links : [],
    version : "2014x-doc-example"
};

moment.tz.createLinks(unlinked);

{
    zones : [
        {name:"Zone/One",abbrs:["OST","ODT"],offsets:[60,120],untils:[403041600000,417034800000]}
    ],
    links : ["Zone/One|Zone/Two"],
    version : "2014x-doc-example"
}

Это особенно полезно в сочетании с moment.tz.filterYears: правила, которые раньше различали два часовых пояса, могут не попасть в отфильтрованный диапазон лет, и тогда их можно связать, чтобы сэкономить место.

Фильтрация по годам

moment.tz.filterYears(UnpackedZone, Number, Number); // UnpackedZone

По умолчанию Moment Timezone включает все данные из базы часовых поясов IANA. Это данные как минимум за период с 1900 по 2038 год. Начиная с версии 0.5.37, в выпусках содержится ещё больше данных, вплоть до годов после 2400-го. Данные за все эти годы могут быть не нужны в вашем случае.

Для фильтрации данных за пределами заданного диапазона лет можно использовать moment.tz.filterYears.

var all    = { name : "America/Los_Angeles", abbrs : [...], offsets : [...] untils : [...]};
var subset = moment.tz.filterYears(all, 2012, 2016);
all.untils.length;    // 186
subset.untils.length; // 11

Если передан только один год, он будет использоваться как начальный и конечный год.

var all    = { name : "America/Los_Angeles", abbrs : [...], offsets : [...] untils : [...]};
var subset = moment.tz.filterYears(all, 2012);
all.untils.length;    // 186
subset.untils.length; // 3

Возможно, вам подойдут одни из небольших готовых пакетов, доступных на главной странице.

Фильтрация по годам, создание ссылок и упаковка

moment.tz.filterLinkPack(UnpackedBundle, Number, Number); // PackedBundle

Упаковка, создание ссылок и выбор подмножества годов — всё это способы сжать данные для передачи клиенту.

Метод moment.tz.filterLinkPack объединяет все эти действия в один простой интерфейс. Передайте распакованный пакет, начальный и конечный годы, чтобы получить отфильтрованный, связанный и упакованный пакет.

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

© JS Foundation and other contributors
Licensed under the MIT License.
https://momentjs.com/timezone/docs/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API