Час12/Час24

Часовые форматы в JavaScript через Intl.DateTimeFormat определяются комбинацией локали и параметров форматирования, среди которых ключевую роль играют hour12 и hourCycle. Эти опции управляют тем, отображается ли время в 12-часовом или 24-часовом виде, а также каким образом интерпретируется переход между полуночью и полуднем.

Intl.DateTimeFormat работает на основе локали и набора опций, формируя строковое представление даты и времени в соответствии с культурными нормами.

Базовый пример:

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

console.log(formatter.format(new Date()));

В данном случае формат времени определяется локалью ru-RU, которая по умолчанию использует 24-часовую систему.

Ключевые поля, влияющие на время:

  • hour — отображение часов
  • minute — минуты
  • second — секунды
  • hour12 — переключатель 12/24-часового формата
  • hourCycle — более точная настройка цикла часов

Параметр hour12

hour12 задаёт бинарное поведение:

  • true — 12-часовой формат (AM/PM)
  • false — 24-часовой формат

Пример 12-часового формата:

const f12 = new Intl.DateTimeFormat('en-US', {
  hour: '2-digit',
  minute: '2-digit',
  hour12: true
});

console.log(f12.format(new Date()));

Пример 24-часового формата:

const f24 = new Intl.DateTimeFormat('en-GB', {
  hour: '2-digit',
  minute: '2-digit',
  hour12: false
});

console.log(f24.format(new Date()));

Особенность hour12 заключается в том, что он не всегда является единственным источником истины. В ряде локалей он игнорируется, если явно задан hourCycle.

Параметр hourCycle

hourCycle — более низкоуровневая и точная настройка часового цикла. Он определяет, как именно интерпретируется диапазон часов.

Доступные значения:

  • h11 — 0–11 (с полуночью 0)
  • h12 — 1–12 (с полуднем 12)
  • h23 — 0–23 (классический 24-часовой формат)
  • h24 — 1–24 (редко используемый вариант)

Пример использования:

const fmt = new Intl.DateTimeFormat('en-US', {
  hour: '2-digit',
  minute: '2-digit',
  hourCycle: 'h23'
});

console.log(fmt.format(new Date()));

hourCycle имеет приоритет над hour12. Если оба указаны, используется именно hourCycle.

Разница между hour12 и hourCycle

hour12 — абстракция высокого уровня, ориентированная на удобство.

hourCycle — точное описание математической модели времени.

Сопоставление:

hour12 hourCycle Поведение
true h12 / h11 12-часовой формат
false h23 / h24 24-часовой формат

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

Влияние локали на формат времени

Локаль может переопределять поведение форматирования даже без явного указания hour12.

Пример:

const a = new Intl.DateTimeFormat('en-US', {
  hour: '2-digit',
  minute: '2-digit'
});

const b = new Intl.DateTimeFormat('ru-RU', {
  hour: '2-digit',
  minute: '2-digit'
});
  • en-US → обычно 12-часовой формат
  • ru-RU → обычно 24-часовой формат

Если не задано явно, движок выбирает формат по культурным правилам BCP 47.

Поле dayPeriod и AM/PM

В 12-часовом формате появляется дополнительная семантика:

const fmt = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hour12: true
});

console.log(fmt.format(new Date()));

Результат включает AM/PM, который формируется через dayPeriod:

  • AM — до полудня
  • PM — после полудня

В некоторых локалях вместо AM/PM используются текстовые периоды:

  • “morning”
  • “afternoon”
  • “evening”

formatToParts и разбор структуры времени

Метод formatToParts позволяет получить структурированное представление времени, включая компоненты цикла часов.

const fmt = new Intl.DateTimeFormat('en-US', {
  hour: '2-digit',
  minute: '2-digit',
  hour12: true
});

console.log(fmt.formatToParts(new Date()));

Выход:

[
  { type: 'hour', value: '07' },
  { type: 'literal', value: ':' },
  { type: 'minute', value: '30' },
  { type: 'literal', value: ' ' },
  { type: 'dayPeriod', value: 'PM' }
]

Это особенно важно при построении кастомных UI-компонентов часов, где требуется контроль над каждым элементом строки.

Влияние timeZone на отображение часов

Часовой формат тесно связан с временной зоной:

const fmt = new Intl.DateTimeFormat('en-GB', {
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'UTC',
  hourCycle: 'h23'
});

console.log(fmt.format(new Date()));

timeZone не влияет на выбор 12/24-часового формата напрямую, но изменяет исходное значение времени, к которому применяется форматирование.

Пограничные случаи полуночи и полудня

Разные циклы часов по-разному трактуют критические значения:

  • h11: 0–11, где 0 — полночь
  • h12: 1–12, где 12 используется для полудня
  • h23: 0–23, стандартный 24-часовой цикл
  • h24: 1–24, где 24 соответствует полуночи следующего дня

Пример различий:

const d = new Date('2024-01-01T00:00:00');

console.log(new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  hourCycle: 'h12'
}).format(d));

console.log(new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  hourCycle: 'h23'
}).format(d));

Приоритет параметров форматирования

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

  1. Проверяется hourCycle
  2. Если отсутствует — используется hour12
  3. Если отсутствует — применяется локаль
  4. Если локаль не определяет формат — используется системный дефолт

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

Совместное использование с другими временными полями

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

const fmt = new Intl.DateTimeFormat('de-DE', {
  weekday: 'long',
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hourCycle: 'h23'
});

Комбинация позволяет формировать полные временные метки с локализованным представлением.

Сравнение 12- и 24-часовых форматов в разных локалях

Поведение по регионам:

  • США (en-US) → 12-часовой формат по умолчанию
  • Великобритания (en-GB) → 24-часовой формат в большинстве случаев
  • Германия (de-DE) → 24-часовой формат
  • Индия (en-IN) → часто 12-часовой формат с AM/PM
  • Россия (ru-RU) → 24-часовой формат

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

Кастомизация вывода времени через Intl

Для полного контроля используется комбинация:

  • hourCycle
  • formatToParts
  • ручная сборка строки

Пример:

const fmt = new Intl.DateTimeFormat('en-US', {
  hour: '2-digit',
  minute: '2-digit',
  hourCycle: 'h12'
});

const parts = fmt.formatToParts(new Date());

const result = parts
  .map(p => p.value)
  .join('');

console.log(result);

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

Поведение в разных средах выполнения

Различия могут проявляться в:

  • Node.js версий с разными ICU данными
  • браузерах с урезанными локалями
  • системных настройках ОС

Intl опирается на ICU (International Components for Unicode), поэтому поддержка hourCycle и локалей зависит от версии ICU в среде выполнения.