Intl.DurationFormat API
Форматируйте временные интервалы в JavaScript с автоматической локализацией
Введение
Когда вы показываете, сколько времени что-то занимает, нужно форматировать значения длительности для пользователей. Видеоплеер показывает продолжительность, сервис бронирования авиабилетов — время в пути, фитнес-приложение — длительность тренировки. Без локализации код может выглядеть так:
const hours = 1;
const minutes = 46;
const seconds = 40;
const duration = `${hours}h ${minutes}m ${seconds}s`;
В результате все пользователи видят "1h 46m 40s" независимо от языка. Французские пользователи видят "1h 46m 40s", хотя ожидают "1 h 46 min 40 s". Немцы видят те же английские сокращения. Испанцы не получают союз "y" между единицами.
API Intl.DurationFormat решает эту проблему. Она форматирует временные интервалы в соответствии с языком и культурными нормами пользователя — без сторонних библиотек и ручной сборки строк.
const duration = { hours: 1, minutes: 46, seconds: 40 };
new Intl.DurationFormat('fr-FR', { style: 'short' }).format(duration);
// "1 h, 46 min et 40 s"
Форматтер автоматически учитывает сокращения, союзы, порядок слов и пробелы для любого языка.
Что такое длительности
Длительность — это промежуток времени, а не конкретный момент. Даты и время указывают, когда что-то происходит. Длительности измеряют, сколько времени что-то занимает.
Это различие важно для форматирования. Даты связаны с календарями, часовыми поясами и историческими правилами. Длительности проще: они измеряют прошедшее время в стандартных единицах без календарного контекста.
API Intl.DurationFormat работает с длительностями. API Intl.DateTimeFormat — с датами и временем. Используйте подходящий инструмент для каждой задачи.
Как создать форматтер длительности
Конструктор принимает локаль и объект с опциями. Локаль определяет язык вывода. Опции управляют стилем форматирования и отображением единиц.
const formatter = new Intl.DurationFormat('en', { style: 'long' });
Вызовите format() с объектом длительности. В объекте должны быть числовые свойства для нужных единиц времени. Включайте только те единицы, которые хотите показать.
const duration = { hours: 2, minutes: 30 };
formatter.format(duration);
// "2 hours and 30 minutes"
API поддерживает следующие единицы времени: years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds. Используйте те единицы, которые подходят для ваших данных.
Выберите стиль форматирования
Опция style управляет плотностью вывода. Доступно четыре стиля: long, short, narrow и digital.
Длинный стиль использует полные слова. Используйте его для обычного текста и доступности.
const duration = { hours: 1, minutes: 46, seconds: 40 };
new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "1 hour, 46 minutes and 40 seconds"
Краткий стиль использует распространённые сокращения. Используйте его, когда мало места, но важна читаемость.
new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "1 hr, 46 min and 40 sec"
Узкий стиль использует минимум символов. Подходит для компактных интерфейсов, например, на мобильных.
new Intl.DurationFormat('en', { style: 'narrow' }).format(duration);
// "1h 46m 40s"
Цифровой стиль выводит время как таймер с двоеточиями. Используйте его для медиаплееров и таймеров обратного отсчёта.
new Intl.DurationFormat('en', { style: 'digital' }).format(duration);
// "1:46:40"
Локализация для разных языков
Одинаковая длительность форматируется по-разному в каждом языке. API автоматически обрабатывает всю локализацию.
const duration = { hours: 1, minutes: 46, seconds: 40 };
new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "1 hour, 46 minutes and 40 seconds"
new Intl.DurationFormat('fr', { style: 'long' }).format(duration);
// "1 heure, 46 minutes et 40 secondes"
new Intl.DurationFormat('de', { style: 'long' }).format(duration);
// "1 Stunde, 46 Minuten und 40 Sekunden"
new Intl.DurationFormat('es', { style: 'long' }).format(duration);
// "1 hora, 46 minutos y 40 segundos"
new Intl.DurationFormat('ja', { style: 'long' }).format(duration);
// "1時間46分40秒"
Обратите внимание, что в каждом языке используются разные слова, сокращения и союзы. Во французском — «et», в немецком — «und», в испанском — «y», в японском союзы не используются. API знает эти правила.
Краткий и узкий стили тоже правильно локализуются.
new Intl.DurationFormat('fr', { style: 'short' }).format(duration);
// "1 h, 46 min et 40 s"
new Intl.DurationFormat('de', { style: 'narrow' }).format(duration);
// "1 Std. 46 Min. 40 Sek."
Собирайте объекты длительности из вычислений времени
Объекты длительности — это обычные JavaScript-объекты со свойствами единиц времени. Создавайте их из любых вычислений времени.
Преобразуйте миллисекунды в единицы длительности, деля на соответствующие коэффициенты.
const milliseconds = 6400000; // 1 hour, 46 minutes, 40 seconds
const hours = Math.floor(milliseconds / 3600000);
const minutes = Math.floor((milliseconds % 3600000) / 60000);
const seconds = Math.floor((milliseconds % 60000) / 1000);
const duration = { hours, minutes, seconds };
new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "1 hr, 46 min and 40 sec"
Вычисляйте длительность между двумя датами, вычитая метки времени.
const start = new Date('2025-01-01T10:00:00');
const end = new Date('2025-01-01T11:46:40');
const diffMs = end - start;
const hours = Math.floor(diffMs / 3600000);
const minutes = Math.floor((diffMs % 3600000) / 60000);
const seconds = Math.floor((diffMs % 60000) / 1000);
const duration = { hours, minutes, seconds };
Включайте только нужные вам единицы. Пропускайте нулевые значения, если не хотите их отображать.
const duration = { minutes: 5, seconds: 30 };
new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "5 minutes and 30 seconds"
Управляйте отображением единиц
По умолчанию форматтер показывает все единицы, которые вы указываете в объекте duration. Управляйте отображением единиц с помощью объекта options.
Укажите стиль отображения для каждой единицы отдельно. Доступные опции: long, short, narrow, numeric и 2-digit.
const duration = { hours: 1, minutes: 5, seconds: 3 };
new Intl.DurationFormat('en', {
hours: 'long',
minutes: 'numeric',
seconds: '2-digit'
}).format(duration);
// "1 hour, 5:03"
Стиль numeric показывает число без подписей. Стиль 2-digit добавляет ведущие нули. Используйте их для компактного отображения, где смешиваются текст и числа.
Скрывайте единицы, просто не указывая их ни в объекте duration, ни в options.
const duration = { hours: 2, minutes: 30 };
new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "2 hr and 30 min"
Для цифрового стиля требуется, чтобы все единицы от самой крупной до самой мелкой были указаны или явно настроены.
const duration = { minutes: 5, seconds: 30 };
new Intl.DurationFormat('en', { style: 'digital' }).format(duration);
// "5:30"
Форматирование длительности видео
Видеоплееры показывают длительность в элементах управления. Используйте узкий или цифровой стиль для компактного отображения.
function formatVideoDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
const duration = hours > 0
? { hours, minutes, seconds: secs }
: { minutes, seconds: secs };
const locale = navigator.language;
return new Intl.DurationFormat(locale, { style: 'digital' }).format(duration);
}
formatVideoDuration(6400); // "1:46:40"
formatVideoDuration(330); // "5:30"
Это позволяет корректно отображать как короткие, так и длинные видео, при необходимости добавляя часы.
Форматирование времени в пути и перелётов
В интерфейсах бронирования показывается длительность поездки. Используйте короткий стиль для лучшей читаемости в ограниченном пространстве.
function formatFlightDuration(departureDate, arrivalDate, locale) {
const diffMs = arrivalDate - departureDate;
const hours = Math.floor(diffMs / 3600000);
const minutes = Math.floor((diffMs % 3600000) / 60000);
const duration = { hours, minutes };
return new Intl.DurationFormat(locale, { style: 'short' }).format(duration);
}
const departure = new Date('2025-06-15T10:30:00');
const arrival = new Date('2025-06-15T18:45:00');
formatFlightDuration(departure, arrival, 'en');
// "8 hr and 15 min"
formatFlightDuration(departure, arrival, 'fr');
// "8 h et 15 min"
Форматирование длительности тренировок и активности
Фитнес-приложения отслеживают длительность упражнений. Используйте длинный стиль для итогов сессии и узкий стиль для списков.
function formatWorkoutDuration(startTime, endTime, locale) {
const diffMs = endTime - startTime;
const hours = Math.floor(diffMs / 3600000);
const minutes = Math.floor((diffMs % 3600000) / 60000);
const duration = hours > 0
? { hours, minutes }
: { minutes };
return new Intl.DurationFormat(locale, { style: 'long' }).format(duration);
}
const workoutStart = new Date('2025-06-15T07:00:00');
const workoutEnd = new Date('2025-06-15T08:15:00');
formatWorkoutDuration(workoutStart, workoutEnd, 'en');
// "1 hour and 15 minutes"
Получение форматированных частей для кастомного отображения
Метод formatToParts() возвращает массив объектов, представляющих каждую часть форматированного вывода. Используйте это для стилизации отдельных частей или создания своих макетов.
const duration = { hours: 1, minutes: 46, seconds: 40 };
const parts = new Intl.DurationFormat('en', { style: 'long' }).formatToParts(duration);
Каждая часть содержит type и value. Типы включают hour, minute, second, literal и подписи единиц, такие как hourUnit, minuteUnit.
[
{ type: "integer", value: "1" },
{ type: "literal", value: " " },
{ type: "unit", value: "hour" },
{ type: "literal", value: ", " },
{ type: "integer", value: "46" },
{ type: "literal", value: " " },
{ type: "unit", value: "minutes" },
{ type: "literal", value: " and " },
{ type: "integer", value: "40" },
{ type: "literal", value: " " },
{ type: "unit", value: "seconds" }
]
Пройдитесь по частям, чтобы применить кастомное оформление.
function StyledDuration({ duration }) {
const parts = new Intl.DurationFormat('en', { style: 'long' }).formatToParts(duration);
return parts.map((part, i) => {
if (part.type === 'integer') {
return <strong key={i}>{part.value}</strong>;
}
return <span key={i}>{part.value}</span>;
});
}
Переиспользуйте экземпляры форматтера для производительности
Создание нового форматтера для каждой длительности увеличивает нагрузку. Создайте форматтер один раз и используйте повторно.
const formatter = new Intl.DurationFormat('en', { style: 'short' });
const durations = [
{ hours: 1, minutes: 30 },
{ hours: 2, minutes: 15 },
{ hours: 0, minutes: 45 }
];
durations.map(d => formatter.format(d));
// ["1 hr and 30 min", "2 hr and 15 min", "45 min"]
Такой подход ускоряет работу при форматировании множества длительностей в циклах или рендерах.
Миграция с ручной сборки строк
Замените ручную конкатенацию строк на API. Это уменьшит код и добавит локализацию.
До:
function formatDuration(hours, minutes) {
if (hours > 0 && minutes > 0) {
return `${hours}h ${minutes}m`;
} else if (hours > 0) {
return `${hours}h`;
} else {
return `${minutes}m`;
}
}
После:
function formatDuration(hours, minutes, locale) {
const duration = {};
if (hours > 0) duration.hours = hours;
if (minutes > 0) duration.minutes = minutes;
return new Intl.DurationFormat(locale, { style: 'narrow' }).format(duration);
}
Версия с API автоматически обрабатывает всю логику условий и поддерживает несколько языков.
Миграция с библиотек
Библиотеки вроде Moment.js и date-fns умеют форматировать длительности, но увеличивают размер бандла. Нативный API избавляет от этой зависимости.
Замените форматирование длительности через Moment.js:
// Before: Moment.js
const duration = moment.duration(6400, 'seconds');
const formatted = duration.hours() + 'h ' + duration.minutes() + 'm';
// After: Intl.DurationFormat
const duration = {
hours: Math.floor(6400 / 3600),
minutes: Math.floor((6400 % 3600) / 60)
};
new Intl.DurationFormat('en', { style: 'narrow' }).format(duration);
Замените форматирование длительности через date-fns:
// Before: date-fns
import { formatDuration, intervalToDuration } from 'date-fns';
const duration = intervalToDuration({ start: 0, end: 6400000 });
const formatted = formatDuration(duration);
// After: Intl.DurationFormat
const ms = 6400000;
const duration = {
hours: Math.floor(ms / 3600000),
minutes: Math.floor((ms % 3600000) / 60000),
seconds: Math.floor((ms % 60000) / 1000)
};
new Intl.DurationFormat('en', { style: 'long' }).format(duration);
Нативный API даёт тот же функционал без зависимостей.
Поддержка браузеров и полифиллы
API Intl.DurationFormat стал Baseline в марте 2025 года. Работает в последних версиях Chrome, Edge, Firefox и Safari.
Проверьте поддержку перед использованием:
if (typeof Intl.DurationFormat !== 'undefined') {
const formatter = new Intl.DurationFormat('en', { style: 'short' });
return formatter.format(duration);
} else {
// Fallback for older browsers
return `${duration.hours}h ${duration.minutes}m`;
}
Для более широкой поддержки используйте polyfill. Проект FormatJS предоставляет @formatjs/intl-durationformat.
npm install @formatjs/intl-durationformat
Импортируйте и добавьте polyfill, если нужно:
if (!Intl.DurationFormat) {
await import('@formatjs/intl-durationformat/polyfill');
}
Polyfill добавляет примерно 15 КБ в сжатом виде. Загружайте его только при необходимости, чтобы не перегружать современные браузеры.
Когда использовать DurationFormat
Используйте Intl.DurationFormat, когда нужно показывать прошедшее время, оставшееся время или длительность. Это актуально для видеоплееров, таймеров, обратных отсчётов, трекеров, бронирования поездок и отображения длительности сессий.
Не используйте его для дат, времени или временных меток. Для этого используйте Intl.DateTimeFormat. Не используйте его для выражений относительного времени, например, «2 часа назад». Для этого используйте Intl.RelativeTimeFormat.
Этот API форматирует длительности, а не вычисляет их. Вам нужно самостоятельно рассчитывать значения длительности на основе дат, временных меток или других источников. Форматтер отвечает только за отображение.