API Intl.DateTimeFormat

Форматируйте даты и время для любого региона без внешних библиотек

Введение

Формат отображения дат различается по всему миру. 15 января 2024 года отображается как 1/15/2024 в Соединенных Штатах, 15/1/2024 в Великобритании и 2024/1/15 в Японии. Форматы времени также различаются. Американцы используют 12-часовые часы с обозначениями AM и PM, в то время как большинство стран мира используют 24-часовой формат.

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

API Intl.DateTimeFormat решает эту проблему. Оно предоставляет встроенное форматирование дат и времени с учетом локали во всех современных браузерах. Внешние библиотеки не требуются.

Основы использования

Самый простой способ отформатировать дату — создать экземпляр Intl.DateTimeFormat и вызвать его метод format().

const date = new Date(2024, 0, 15, 14, 30);

const formatter = new Intl.DateTimeFormat("en-US");
formatter.format(date);
// "1/15/2024"

Измените локаль, чтобы увидеть разные форматы.

const ukFormatter = new Intl.DateTimeFormat("en-GB");
ukFormatter.format(date);
// "15/01/2024"

const japanFormatter = new Intl.DateTimeFormat("ja-JP");
japanFormatter.format(date);
// "2024/1/15"

Одна и та же дата, отформатированная тремя разными способами в зависимости от местных стандартов.

Понимание локалей

Локаль — это строка, которая идентифицирует язык и региональные предпочтения. Формат соответствует стандарту BCP 47: код языка, за которым может следовать код региона.

Примеры распространенных локалей:

"en"      // Английский (общий)
"en-US"   // Английский (США)
"en-GB"   // Английский (Великобритания)
"es"      // Испанский (общий)
"es-MX"   // Испанский (Мексика)
"es-ES"   // Испанский (Испания)
"zh-CN"   // Китайский (Китай, упрощенный)
"zh-TW"   // Китайский (Тайвань, традиционный)

Если вы не укажете параметр локали, браузер использует локаль пользователя по умолчанию.

const formatter = new Intl.DateTimeFormat();
formatter.format(date);
// Вывод зависит от локали браузера пользователя

Вы также можете указать массив локалей. Браузер использует первую поддерживаемую локаль.

const formatter = new Intl.DateTimeFormat(["es-MX", "es", "en"]);
// Использует испанский (Мексика), если доступен, затем общий испанский, затем английский

Обзор параметров форматирования

Intl.DateTimeFormat принимает объект с параметрами, который определяет, что будет отображаться в отформатированном выводе. Существует два подхода к форматированию.

Первый подход использует сокращённые стили. Они обеспечивают быстрое и стандартное форматирование.

const formatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "full",
  timeStyle: "short"
});

formatter.format(date);
// "Monday, January 15, 2024 at 2:30 PM"

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

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});

formatter.format(date);
// "January 15, 2024 at 2:30 PM"

Нельзя смешивать сокращённые стили и параметры компонентов. Выберите один подход для каждого форматтера.

Сокращённые стили

Параметры dateStyle и timeStyle предоставляют четыре уровня предустановленного форматирования.

Параметр dateStyle форматирует часть даты.

const date = new Date(2024, 0, 15);

const full = new Intl.DateTimeFormat("en-US", { dateStyle: "full" });
full.format(date);
// "Monday, January 15, 2024"

const long = new Intl.DateTimeFormat("en-US", { dateStyle: "long" });
long.format(date);
// "January 15, 2024"

const medium = new Intl.DateTimeFormat("en-US", { dateStyle: "medium" });
medium.format(date);
// "Jan 15, 2024"

const short = new Intl.DateTimeFormat("en-US", { dateStyle: "short" });
short.format(date);
// "1/15/24"

Параметр timeStyle форматирует часть времени.

const date = new Date(2024, 0, 15, 14, 30, 45);

const full = new Intl.DateTimeFormat("en-US", { timeStyle: "full" });
full.format(date);
// "2:30:45 PM Eastern Standard Time"

const long = new Intl.DateTimeFormat("en-US", { timeStyle: "long" });
long.format(date);
// "2:30:45 PM EST"

const medium = new Intl.DateTimeFormat("en-US", { timeStyle: "medium" });
medium.format(date);
// "2:30:45 PM"

const short = new Intl.DateTimeFormat("en-US", { timeStyle: "short" });
short.format(date);
// "2:30 PM"

Комбинируйте оба параметра, чтобы форматировать дату и время вместе.

const formatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "medium",
  timeStyle: "short"
});

formatter.format(date);
// "Jan 15, 2024, 2:30 PM"

Сокращённые стили автоматически адаптируются к локальным соглашениям.

const usFormatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "short",
  timeStyle: "short"
});
usFormatter.format(date);
// "1/15/24, 2:30 PM"

const deFormatter = new Intl.DateTimeFormat("de-DE", {
  dateStyle: "short",
  timeStyle: "short"
});
deFormatter.format(date);
// "15.1.24, 14:30"

Немецкий формат использует точки в качестве разделителей, меняет местами день и месяц и отображает 24-часовой формат времени. Американский формат использует слэши, порядок месяц-день и 12-часовой формат с AM/PM. Одни и те же параметры, но разный вывод в зависимости от локали.

Параметры компонента

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

Параметр year отображает год.

const formatter = new Intl.DateTimeFormat("en-US", { year: "numeric" });
formatter.format(date);
// "2024"

const twoDigit = new Intl.DateTimeFormat("en-US", { year: "2-digit" });
twoDigit.format(date);
// "24"

Параметр month отображает месяц в различных форматах.

const numeric = new Intl.DateTimeFormat("en-US", { month: "numeric" });
numeric.format(date);
// "1"

const twoDigit = new Intl.DateTimeFormat("en-US", { month: "2-digit" });
twoDigit.format(date);
// "01"

const long = new Intl.DateTimeFormat("en-US", { month: "long" });
long.format(date);
// "January"

const short = new Intl.DateTimeFormat("en-US", { month: "short" });
short.format(date);
// "Jan"

const narrow = new Intl.DateTimeFormat("en-US", { month: "narrow" });
narrow.format(date);
// "J"

Параметр day отображает день месяца.

const formatter = new Intl.DateTimeFormat("en-US", { day: "numeric" });
formatter.format(date);
// "15"

const twoDigit = new Intl.DateTimeFormat("en-US", { day: "2-digit" });
twoDigit.format(date);
// "15"

Параметр weekday отображает день недели.

const long = new Intl.DateTimeFormat("en-US", { weekday: "long" });
long.format(date);
// "Monday"

const short = new Intl.DateTimeFormat("en-US", { weekday: "short" });
short.format(date);
// "Mon"

const narrow = new Intl.DateTimeFormat("en-US", { weekday: "narrow" });
narrow.format(date);
// "M"

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

const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});

formatter.format(date);
// "Monday, January 15, 2024"

Браузер обрабатывает пробелы и пунктуацию в соответствии с локальными стандартами.

Форматирование времени

Опции компонента времени управляют отображением часов, минут и секунд.

const date = new Date(2024, 0, 15, 14, 30, 45);

const formatter = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  second: "numeric"
});

formatter.format(date);
// "2:30:45 PM"

Опция hour12 управляет отображением времени в 12-часовом или 24-часовом формате.

const hour12 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  hour12: true
});
hour12.format(date);
// "2:30 PM"

const hour24 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  hour12: false
});
hour24.format(date);
// "14:30"

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

// h11: 0-11
const h11 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h11"
});
h11.format(new Date(2024, 0, 15, 0, 30));
// "0:30 AM"

// h12: 1-12
const h12 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h12"
});
h12.format(new Date(2024, 0, 15, 0, 30));
// "12:30 AM"

// h23: 0-23
const h23 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h23"
});
h23.format(new Date(2024, 0, 15, 0, 30));
// "0:30"

// h24: 1-24
const h24 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h24"
});
h24.format(new Date(2024, 0, 15, 0, 30));
// "24:30"

Разница имеет значение в полночь. Цикл h11 использует 0, в то время как h12 использует 12. Аналогично, h23 использует 0, а h24 использует 24.

Опция dayPeriod добавляет описательные маркеры периода для 12-часового формата времени.

const morning = new Date(2024, 0, 15, 10, 30);
const afternoon = new Date(2024, 0, 15, 14, 30);
const night = new Date(2024, 0, 15, 22, 30);

const formatter = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  dayPeriod: "long"
});

formatter.format(morning);
// "10:30 in the morning"

formatter.format(afternoon);
// "2:30 in the afternoon"

formatter.format(night);
// "10:30 at night"

Опция dayPeriod работает только с 12-часовыми форматами времени.

Опция fractionalSecondDigits отображает точность до долей секунды.

const date = new Date(2024, 0, 15, 14, 30, 45, 123);

const oneDigit = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  second: "numeric",
  fractionalSecondDigits: 1
});
oneDigit.format(date);
// "2:30:45.1 PM"

const threeDigits = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  second: "numeric",
  fractionalSecondDigits: 3
});
threeDigits.format(date);
// "2:30:45.123 PM"

Вы можете указать одну, две или три дробные цифры.

Обработка часовых поясов

Опция timeZone преобразует даты в определённые часовые пояса.

const date = new Date("2024-01-15T14:30:00Z"); // Время UTC

const newYork = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  dateStyle: "full",
  timeStyle: "long"
});
newYork.format(date);
// "Monday, January 15, 2024 at 9:30:00 AM EST"

const tokyo = new Intl.DateTimeFormat("ja-JP", {
  timeZone: "Asia/Tokyo",
  dateStyle: "full",
  timeStyle: "long"
});
tokyo.format(date);
// "2024年1月15日月曜日 23:30:00 日本標準時"

const london = new Intl.DateTimeFormat("en-GB", {
  timeZone: "Europe/London",
  dateStyle: "full",
  timeStyle: "long"
});
london.format(date);
// "Monday, 15 January 2024 at 14:30:00 GMT"

Один и тот же момент времени отображается по-разному в зависимости от часового пояса. Используйте идентификаторы часовых поясов IANA, такие как America/New_York, Europe/London или Asia/Tokyo.

Опция timeZoneName отображает название часового пояса.

const formatter = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "short"
});
formatter.format(date);
// "9:30 AM EST"

const long = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "long"
});
long.format(date);
// "9:30 AM Eastern Standard Time"

const shortOffset = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "shortOffset"
});
shortOffset.format(date);
// "9:30 AM GMT-5"

const longOffset = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "longOffset"
});
longOffset.format(date);
// "9:30 AM GMT-05:00"

Различные значения timeZoneName предоставляют разный уровень детализации.

Календарные и числовые системы

Опция calendar поддерживает некатолические календари.

const date = new Date(2024, 0, 15);

const gregorian = new Intl.DateTimeFormat("en-US", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});
gregorian.format(date);
// "January 15, 2024"

const japanese = new Intl.DateTimeFormat("ja-JP", {
  calendar: "japanese",
  year: "numeric",
  month: "long",
  day: "numeric"
});
japanese.format(date);
// "令和6年1月15日"

const islamic = new Intl.DateTimeFormat("ar-SA", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});
islamic.format(date);
// "٦ رجب ١٤٤٥"

const chinese = new Intl.DateTimeFormat("zh-CN", {
  calendar: "chinese",
  year: "numeric",
  month: "long",
  day: "numeric"
});
chinese.format(date);
// "2023甲辰年腊月初五"

Одна и та же дата преобразуется в разные календарные системы. Доступные календари включают gregory, japanese, islamic, islamic-umalqura, islamic-tbla, islamic-civil, islamic-rgsa, chinese, hebrew, indian, persian и другие.

Опция numberingSystem отображает цифры в разных системах счисления.

const date = new Date(2024, 0, 15);

const western = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "latn",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
western.format(date);
// "1/15/2024"

const arabic = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "arab",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
arabic.format(date);
// "١‏/١٥‏/٢٠٢٤"

const devanagari = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "deva",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
devanagari.format(date);
// "१/१५/२०२४"

const bengali = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "beng",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
bengali.format(date);
// "১/১৫/২০২৪"

Доступные системы счисления включают latn (западные цифры), arab (арабско-индийские цифры), arabext (расширенные арабско-индийские цифры), beng (бенгальские цифры), deva (деванагари) и многие другие.

Форматирование диапазонов дат

Метод formatRange() форматирует диапазон дат с умным пропуском избыточной информации.

const start = new Date(2024, 0, 15);
const end = new Date(2024, 0, 20);

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

formatter.formatRange(start, end);
// "15 – 20 января 2024 г."

Форматировщик пропускает повторяющуюся информацию. Обе даты находятся в январе 2024 года, поэтому месяц и год указываются только один раз.

Если диапазон охватывает разные месяцы, оба отображаются.

const start = new Date(2024, 0, 15);
const end = new Date(2024, 1, 20);

formatter.formatRange(start, end);
// "15 января – 20 февраля 2024 г."

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

const start = new Date(2024, 0, 15);
const end = new Date(2025, 1, 20);

formatter.formatRange(start, end);
// "15 января 2024 г. – 20 февраля 2025 г."

Метод formatRange() также работает с диапазонами времени.

const start = new Date(2024, 0, 15, 14, 30);
const end = new Date(2024, 0, 15, 16, 45);

const formatter = new Intl.DateTimeFormat("en-US", {
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});

formatter.formatRange(start, end);
// "15 января, 14:30 – 16:45"

Дата отображается один раз, так как оба времени относятся к одному дню.

Доступ к форматированным частям

Метод formatToParts() возвращает массив объектов, представляющих каждую часть форматированной даты. Это позволяет реализовать пользовательскую логику форматирования.

const date = new Date(2024, 0, 15, 14, 30);

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});

const parts = formatter.formatToParts(date);

Каждый объект в массиве имеет свойства type и value.

[
  { type: "month", value: "января" },
  { type: "literal", value: " " },
  { type: "day", value: "15" },
  { type: "literal", value: ", " },
  { type: "year", value: "2024" },
  { type: "literal", value: " в " },
  { type: "hour", value: "14" },
  { type: "literal", value: ":" },
  { type: "minute", value: "30" }
]

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

const dateParts = parts.filter(part =>
  ["month", "day", "year"].includes(part.type)
);

const dateString = dateParts.map(part => part.value).join("/");
// "января/15/2024"

Метод formatRangeToParts() предоставляет ту же функциональность для диапазонов дат.

const start = new Date(2024, 0, 15);
const end = new Date(2024, 0, 20);

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const parts = formatter.formatRangeToParts(start, end);

Каждый объект части включает свойство source, указывающее, относится ли он к начальной или конечной дате.

[
  { type: "month", value: "января", source: "startRange" },
  { type: "literal", value: " ", source: "startRange" },
  { type: "day", value: "15", source: "startRange" },
  { type: "literal", value: " – ", source: "shared" },
  { type: "day", value: "20", source: "endRange" },
  { type: "literal", value: ", ", source: "shared" },
  { type: "year", value: "2024", source: "shared" }
]

Лучшие практики

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

// Менее эффективно
dates.forEach(date => {
  const formatted = new Intl.DateTimeFormat("en-US").format(date);
  console.log(formatted);
});

// Более эффективно
const formatter = new Intl.DateTimeFormat("en-US");
dates.forEach(date => {
  const formatted = formatter.format(date);
  console.log(formatted);
});

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

const formatter = new Intl.DateTimeFormat();
// Автоматически использует локаль браузера

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

const formatter = new Intl.DateTimeFormat(["fr-CA", "fr", "en"]);
// Предпочитает французский (Канада), затем французский, затем английский

Используйте сокращения стилей для отображения дат и времени. Они автоматически адаптируются к соглашениям локали и требуют меньше настроек.

const formatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "medium",
  timeStyle: "short"
});

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

const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});

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

const formatter = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  dateStyle: "full",
  timeStyle: "long"
});

Используйте formatRange() для диапазонов дат вместо форматирования каждой даты отдельно и их объединения. Этот метод умно исключает избыточную информацию.

// Менее понятно
const startFormatted = formatter.format(start);
const endFormatted = formatter.format(end);
const range = `${startFormatted} to ${endFormatted}`;

// Лучше
const range = formatter.formatRange(start, end);

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