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

Диапазоны дат используются, когда требуется представить временной интервал в человекочитаемом виде: период отпуска, рабочие смены, события, дедлайны, расписания. В JavaScript для этого применяется расширение Intl.DateTimeFormat, предоставляющее метод formatRange, который позволяет корректно форматировать начало и конец интервала с учётом локали и правил сокращения.


Базовый механизм formatRange

Метод formatRange доступен у экземпляра Intl.DateTimeFormat и принимает две даты:

const formatter = new Intl.DateTimeFormat('ru-RU');

const start = new Date(2026, 0, 1);
const end = new Date(2026, 0, 10);

console.log(formatter.formatRange(start, end));

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

Ключевая особенность

Intl.DateTimeFormat автоматически:

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

Сокращение повторяющихся компонентов

Одно из главных преимуществ formatRange — интеллектуальное сокращение повторяющихся элементов.

Пример с одинаковым годом и месяцем

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

const start = new Date(2026, 0, 1);
const end = new Date(2026, 0, 10);

console.log(formatter.formatRange(start, end));

Результат будет наподобие:

1–10 января 2026 г.

Повторяющиеся элементы (января 2026 г.) выводятся один раз.


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

Алгоритм форматирования зависит от того, какие части даты различаются.

Различаются только дни

const start = new Date(2026, 0, 1);
const end = new Date(2026, 0, 2);

Вывод сокращается до:

1–2 января 2026 г.

Различаются месяцы

const start = new Date(2026, 0, 25);
const end = new Date(2026, 1, 5);

Результат:

25 января – 5 февраля 2026 г.

Различаются годы

const start = new Date(2025, 11, 31);
const end = new Date(2026, 0, 1);

Результат:

31 декабря 2025 – 1 января 2026 г.

Управление стилем форматирования

Intl.DateTimeFormat позволяет управлять уровнем детализации через опции.

Полный формат даты

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

Короткий формат

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: '2-digit',
  month: '2-digit',
  day: '2-digit'
});

Пример результата:

01.01.26–10.01.26

Разница между format и formatRange

Метод format используется для одной даты:

formatter.format(new Date());

Метод formatRange предназначен для интервалов:

formatter.formatRange(startDate, endDate);

Использование format для диапазона требует ручной сборки строки:

`${formatter.format(start)} – ${formatter.format(end)}`

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


Поведение при одинаковых датах

Если обе даты совпадают, formatRange возвращает единичное значение:

const date = new Date(2026, 0, 1);

formatter.formatRange(date, date);

Результат:

1 января 2026 г.

Это важное отличие от ручной конкатенации, где дублирование неизбежно.


Работа с временем в диапазонах

Диапазоны могут включать не только даты, но и время.

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit'
});

const start = new Date(2026, 0, 1, 10, 0);
const end = new Date(2026, 0, 1, 12, 30);

console.log(formatter.formatRange(start, end));

Возможный результат:

01.01.2026, 10:00–12:30

Система автоматически убирает повторяющуюся дату.


Локализация диапазонов

Локаль влияет не только на формат, но и на порядок элементов.

Русская локаль

new Intl.DateTimeFormat('ru-RU').formatRange(start, end);
1–10 января 2026 г.

Американская локаль

new Intl.DateTimeFormat('en-US').formatRange(start, end);
Jan 1 – Jan 10, 2026

Поведение formatRangeToParts

Метод formatRangeToParts возвращает структурированное представление диапазона.

const formatter = new Intl.DateTimeFormat('ru-RU');

const parts = formatter.formatRangeToParts(
  new Date(2026, 0, 1),
  new Date(2026, 0, 10)
);

console.log(parts);

Результат представляет собой массив объектов:

[
  { type: 'day', value: '1', source: 'startRange' },
  { type: 'literal', value: '–', source: 'shared' },
  { type: 'day', value: '10', source: 'endRange' },
  { type: 'month', value: 'января', source: 'shared' },
  { type: 'year', value: '2026', source: 'shared' }
]

Значение source

  • startRange — часть начала диапазона
  • endRange — часть конца диапазона
  • shared — общие элементы

Практика форматирования сложных диапазонов

Годовой диапазон

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric'
});

formatter.formatRange(
  new Date(2025, 0, 1),
  new Date(2027, 0, 1)
);

Результат:

2025–2027

Диапазон с разной точностью

const formatter = new Intl.DateTimeFormat('ru-RU', {
  year: 'numeric',
  month: 'short'
});
formatter.formatRange(
  new Date(2026, 0, 1),
  new Date(2026, 5, 1)
);
янв. – июн. 2026 г.

Ограничения и особенности поведения

Алгоритм форматирования диапазонов не всегда симметричен:

  • формат может различаться в зависимости от локали
  • сокращения применяются автоматически и не настраиваются напрямую
  • некоторые комбинации опций могут приводить к неожиданной агрегации элементов

Например, при слишком низкой детализации (только год) месяцы и дни полностью исчезают из вывода.


Взаимодействие с временными зонами

При использовании timeZone форматирование становится детерминированным относительно выбранной зоны.

const formatter = new Intl.DateTimeFormat('ru-RU', {
  timeZone: 'Europe/Moscow',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

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


Использование в интерфейсах

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

  • календарях событий
  • системах бронирования
  • финансовых отчётах (периоды)
  • аналитических панелях
  • логах активности

Единообразное отображение интервалов снижает когнитивную нагрузку и устраняет необходимость ручной нормализации строк.


Сравнение с ручным форматированием

Ручная сборка диапазона:

`${start.toLocaleDateString()} - ${end.toLocaleDateString()}`

Проблемы:

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

formatRange решает эти проблемы через единый механизм интернационализации, встроенный в движок JavaScript.