Отображение дат в UI

Luxon предоставляет слой поверх стандартного Date и API Intl, позволяя формировать стабильное, локализованное и предсказуемое отображение дат в пользовательском интерфейсе. Основная задача при работе с UI — не хранение или вычисление времени, а корректная визуализация: форматирование, локализация, работа с часовыми поясами, адаптация под контекст экрана и сценарий использования.


Отображение даты в UI почти всегда зависит от трёх факторов:

  • локали пользователя
  • контекста (список, карточка, детальная страница)
  • уровня детализации (дата, дата+время, только время)

Luxon оперирует объектом DateTime, который уже содержит информацию о таймзоне и локали, что критично для UI-рендеринга.

import { DateTime } from "luxon";

const dt = DateTime.now();

Форматирование через предустановки

Наиболее безопасный способ отображения дат в UI — использование встроенных пресетов toLocaleString.

dt.toLocaleString(DateTime.DATE_MED);

Распространённые пресеты:

  • DATE_SHORT — 01.05.2026
  • DATE_MED — 1 мая 2026 г.
  • DATE_FULL — 1 мая 2026 года
  • DATE_HUGE — пятница, 1 мая 2026 года
  • TIME_SIMPLE — 14:30
  • DATETIME_MED — 1 мая 2026 г., 14:30
  • DATETIME_FULL — полная дата с часовым поясом

Использование пресетов снижает риск ошибок локализации и обеспечивает единообразие интерфейса.


Локализация и язык интерфейса

Luxon поддерживает настройку локали через метод setLocale.

const dt = DateTime.now().setLocale("ru");

dt.toLocaleString(DateTime.DATE_FULL);

Ключевые особенности:

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

Для UI важно централизованно задавать локаль на уровне приложения, а не отдельных компонентов.


Кастомное форматирование через tokens

Когда предустановок недостаточно, используется toFormat с токенами:

dt.toFormat("dd.MM.yyyy HH:mm");

Основные токены:

  • yyyy — год
  • MM — месяц (двузначный)
  • dd — день
  • HH — часы (24-часовой формат)
  • mm — минуты
  • ss — секунды

Пример для UI-карточки:

dt.toFormat("dd LLL yyyy, HH:mm");

Результат может выглядеть как: 01 мая 2026, 14:30


Форматы для разных зон интерфейса

UI почти всегда требует разных форматов для одного и того же значения.

Список элементов (компактный вид)

dt.toLocaleString(DateTime.DATE_SHORT);

Используется в таблицах, списках, уведомлениях.


Карточка объекта (средняя детализация)

dt.toLocaleString(DateTime.DATETIME_MED);

Используется в превью, лентах, карточках заказов.


Детальная страница (полная информация)

dt.toLocaleString(DateTime.DATETIME_FULL);

Используется для логов, истории, транзакций.


Отображение относительного времени

Для UX-интерфейсов часто требуется не абсолютная дата, а относительная.

dt.toRelative();

Примеры вывода:

  • «5 минут назад»
  • «через 2 дня»

Также доступен календарный формат:

dt.toRelativeCalendar();

Примеры:

  • «вчера»
  • «завтра»
  • «на прошлой неделе»

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


Работа с часовыми поясами в интерфейсе

UI часто отображает время в разных зонах:

dt.setZone("Europe/Almaty").toLocaleString(DateTime.DATETIME_MED);

Ключевые сценарии:

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

Ошибки в UI чаще всего возникают из-за смешивания UTC и локального времени без явного преобразования.


Консистентность между сервером и клиентом

При SSR важно избегать расхождения:

const serverTime = DateTime.utc().toISO();
const clientTime = DateTime.fromISO(serverTime).toLocal();

Рекомендуемая стратегия:

  • хранить время в UTC
  • преобразовывать в локальное только на уровне UI
  • избегать форматирования на сервере

Условное форматирование в зависимости от контекста

UI часто требует динамического выбора формата:

function formatDate(dt) {
  if (dt.diffNow("days").days > -1) {
    return dt.toRelative();
  }
  if (dt.diffNow("years").years < -1) {
    return dt.toLocaleString(DateTime.DATE_FULL);
  }
  return dt.toLocaleString(DateTime.DATE_MED);
}

Такая логика используется в:

  • новостных лентах
  • системах уведомлений
  • чатах

Форматирование времени для списков сообщений

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

dt.toFormat("HH:mm");

или

dt.toRelativeCalendar();

Выбор зависит от возраста сообщения:

  • менее 24 часов — время
  • до недели — «вчера», «позавчера»
  • старше — дата

Работа с неопределёнными и невалидными датами

UI должен учитывать некорректные значения:

const dt = DateTime.fromISO("invalid");

if (!dt.isValid) {
  return "—";
}

Без проверки isValid интерфейс может получить Invalid DateTime, что ломает отображение.


Форматирование с учётом производительности

При массовом рендеринге (таблицы, списки) важно избегать повторного форматирования:

  • кэширование результата toLocaleString
  • предварительное преобразование на уровне данных
  • минимизация вызовов setZone в рендере

Пример:

const formatted = dt.toLocaleString(DateTime.DATETIME_MED);

Интернационализация UI-элементов

Luxon использует Intl, поэтому поведение зависит от окружения браузера или Node.js.

Особенности:

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

Типовые шаблоны UI-отображения

Заголовок события

dt.toLocaleString(DateTime.DATETIME_FULL)

Метка в списке

dt.toLocaleString(DateTime.DATE_SHORT)

Время последнего действия

dt.toRelative()

Внутренний лог

dt.toFormat("yyyy-LL-dd HH:mm:ss.SSS ZZZ")

Адаптивность отображения

UI может изменять формат в зависимости от ширины экрана:

  • мобильный: DATE_SHORT
  • десктоп: DATETIME_MED
  • расширенный режим: DATETIME_FULL
function getFormat(width) {
  if (width < 600) return DateTime.DATE_SHORT;
  if (width < 1200) return DateTime.DATETIME_MED;
  return DateTime.DATETIME_FULL;
}

Стабильность отображения в разных окружениях

Для предотвращения визуальных расхождений:

  • избегается неявный new Date()
  • всегда используется DateTime.fromISO или fromMillis
  • фиксируется zone и locale при создании объекта
DateTime.fromISO(isoString, {
  zone: "utc",
  locale: "ru"
});