API Intl.DurationFormat

Форматируйте временные длительности в 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.

Стиль long использует полные слова. Используйте его для текста и доступности.

const duration = { hours: 1, minutes: 46, seconds: 40 };
new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "1 час, 46 минут и 40 секунд"

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

new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "1 ч, 46 мин и 40 сек"

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

new Intl.DurationFormat('en', { style: 'narrow' }).format(duration);
// "1ч 46м 40с"

Стиль digital создает вывод, похожий на таймер, с использованием двоеточий. Используйте его для медиаплееров и отображения обратного отсчета.

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 час, 46 минут и 40 секунд"

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 знает эти правила.

Стили short и narrow также корректно локализуются.

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 час, 46 минут, 40 секунд
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"

Управление отображением единиц времени

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

Укажите стиль отображения для каждой единицы отдельно. Доступные варианты: 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 добавляет нулевое заполнение. Используйте их для компактного отображения, сочетающего текст и числа.

Скрывайте единицы, исключая их как из объекта длительности, так и из настроек.

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}ч ${minutes}м`;
  } else if (hours > 0) {
    return `${hours}ч`;
  } else {
    return `${minutes}м`;
  }
}

После:

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:

// До: Moment.js
const duration = moment.duration(6400, 'seconds');
const formatted = duration.hours() + 'ч ' + duration.minutes() + 'м';

// После: Intl.DurationFormat
const duration = {
  hours: Math.floor(6400 / 3600),
  minutes: Math.floor((6400 % 3600) / 60)
};
new Intl.DurationFormat('ru', { style: 'narrow' }).format(duration);

Замена форматирования длительности с date-fns:

// До: date-fns
import { formatDuration, intervalToDuration } from 'date-fns';
const duration = intervalToDuration({ start: 0, end: 6400000 });
const formatted = formatDuration(duration);

// После: 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('ru', { style: 'long' }).format(duration);

Нативное API предоставляет ту же функциональность без зависимостей.

Поддержка браузеров и полифилы

API Intl.DurationFormat стало базовым в марте 2025 года. Оно работает в последних версиях Chrome, Edge, Firefox и Safari.

Проверьте поддержку перед использованием:

if (typeof Intl.DurationFormat !== 'undefined') {
  const formatter = new Intl.DurationFormat('ru', { style: 'short' });
  return formatter.format(duration);
} else {
  // Резервный вариант для старых браузеров
  return `${duration.hours}ч ${duration.minutes}м`;
}

Для более широкой поддержки используйте полифил. Проект FormatJS предоставляет @formatjs/intl-durationformat.

npm install @formatjs/intl-durationformat

Импортируйте и добавьте полифил при необходимости:

if (!Intl.DurationFormat) {
  await import('@formatjs/intl-durationformat/polyfill');
}

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

Когда использовать DurationFormat

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

Не используйте его для дат, времени или временных меток. Для этого используйте Intl.DateTimeFormat. Не используйте его для выражений относительного времени, таких как "2 часа назад". Для этого используйте Intl.RelativeTimeFormat.

API форматирует длительности, а не вычисления времени. Вы должны самостоятельно вычислять значения длительности из дат, временных меток или других источников. Форматировщик отвечает только за отображение.