Intl.DateTimeFormat API
Форматируйте даты и время для любого языка без внешних библиотек
Введение
Даты отображаются по-разному в разных странах. 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" // English (generic)
"en-US" // English (United States)
"en-GB" // English (United Kingdom)
"es" // Spanish (generic)
"es-MX" // Spanish (Mexico)
"es-ES" // Spanish (Spain)
"zh-CN" // Chinese (China, Simplified)
"zh-TW" // Chinese (Taiwan, Traditional)
Если не указать параметр локали, браузер использует локаль пользователя по умолчанию.
const formatter = new Intl.DateTimeFormat();
formatter.format(date);
// Output varies based on user's browser locale
Можно также передать массив локалей. Браузер выберет первую поддерживаемую.
const formatter = new Intl.DateTimeFormat(["es-MX", "es", "en"]);
// Uses Spanish (Mexico) if available, falls back to generic Spanish, then English
Обзор параметров форматирования
Конструктор 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 time
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);
// "January 15 – 20, 2024"
Форматтер опускает повторяющуюся информацию. Обе даты находятся в январе 2024 года, поэтому месяц и год выводятся только один раз.
Если диапазон охватывает разные месяцы, оба месяца отображаются.
const start = new Date(2024, 0, 15);
const end = new Date(2024, 1, 20);
formatter.formatRange(start, end);
// "January 15 – February 20, 2024"
Если диапазон охватывает разные годы, отображается вся информация.
const start = new Date(2024, 0, 15);
const end = new Date(2025, 1, 20);
formatter.formatRange(start, end);
// "January 15, 2024 – February 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);
// "January 15, 2:30 – 4:45 PM"
Дата отображается один раз, так как оба времени относятся к одному дню.
Доступ к отформатированным частям
Метод 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: "January" },
{ type: "literal", value: " " },
{ type: "day", value: "15" },
{ type: "literal", value: ", " },
{ type: "year", value: "2024" },
{ type: "literal", value: " at " },
{ type: "hour", value: "2" },
{ type: "literal", value: ":" },
{ type: "minute", value: "30" },
{ type: "literal", value: " " },
{ type: "dayPeriod", value: "PM" }
]
Вы можете фильтровать и изменять эти части для создания своих форматов.
const dateParts = parts.filter(part =>
["month", "day", "year"].includes(part.type)
);
const dateString = dateParts.map(part => part.value).join("/");
// "January/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);
Каждый объект part содержит свойство source, указывающее, относится ли он к начальной или конечной дате.
[
{ type: "month", value: "January", 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" }
]
Лучшие практики
Переиспользуйте экземпляры форматтера при форматировании нескольких дат с одинаковыми опциями. Создание форматтера включает согласование локали и определение опций, что имеет небольшую стоимость по производительности.
// Less efficient
dates.forEach(date => {
const formatted = new Intl.DateTimeFormat("en-US").format(date);
console.log(formatted);
});
// More efficient
const formatter = new Intl.DateTimeFormat("en-US");
dates.forEach(date => {
const formatted = formatter.format(date);
console.log(formatted);
});
Используйте локаль браузера пользователя, если это возможно, просто не указывая параметр локали. Это учитывает предпочтения пользователя.
const formatter = new Intl.DateTimeFormat();
// Uses browser locale automatically
Указывайте запасные локали при таргетировании на определённые регионы. Если предпочтительная локаль недоступна, браузер использует следующий вариант.
const formatter = new Intl.DateTimeFormat(["fr-CA", "fr", "en"]);
// Prefers French (Canada), falls back to French, then English
Используйте сокращённые стили для стандартных отображений даты и времени. Они автоматически подстраиваются под локальные стандарты и требуют меньше настроек.
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() для диапазонов дат вместо форматирования каждой даты по отдельности и их объединения. Этот метод сам убирает лишнюю повторяющуюся информацию.
// Less clear
const startFormatted = formatter.format(start);
const endFormatted = formatter.format(end);
const range = `${startFormatted} to ${endFormatted}`;
// Better
const range = formatter.formatRange(start, end);
Проверьте совместимость браузеров для продвинутых функций, таких как dayPeriod, fractionalSecondDigits и некоторых значений timeZoneName. Все современные браузеры поддерживают основную функциональность, но для новых опций могут понадобиться запасные решения для старых браузеров.