Переопределение поведения

Переопределение поведения в Luxon опирается на несколько уровней абстракции: глобальные настройки (Settings), механизм временных зон (Zone), локализация (locale), а также точечное управление через методы DateTime. Архитектура библиотеки построена так, чтобы базовые сущности оставались неизменяемыми, а изменение поведения происходило через конфигурацию и подмену зависимостей, а не через мутацию объектов.

Центральный механизм изменения поведения — объект Settings. Он задаёт параметры по умолчанию для всех создаваемых экземпляров DateTime.

Установка временной зоны по умолчанию

Поведение всех операций с датой и временем зависит от выбранной зоны. Если зона не указана явно, используется Settings.defaultZone:

import { Settings } from "luxon";

Settings.defaultZone = "Europe/Paris";

После этого любой вызов создания даты без явного указания зоны будет интерпретироваться в указанной временной зоне.

В качестве значения могут использоваться:

  • строка IANA-зоны ("Europe/Moscow", "UTC")
  • объект Zone
  • фиксированная зона

Переопределение локали

Локаль влияет на форматирование дат, названия месяцев, дней недели и поведение Intl:

Settings.defaultLocale = "ru";

Это влияет на:

  • toLocaleString()
  • toFormat()
  • Info (например, названия месяцев)

Переопределение источника текущего времени

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

Settings.now = () => 1700000000000;

Теперь DateTime.now() всегда будет возвращать фиксированное значение.

Такой подход позволяет:

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

Строгий режим ошибок

Settings.throwOnInvalid = true;

При включении любое некорректное создание DateTime приводит к выбросу исключения вместо возврата объекта с состоянием invalid. Это изменяет модель обработки ошибок с «мягкой» на «строгую».


Управление временными зонами

Luxon построен вокруг абстракции Zone. Именно через неё достигается переопределение поведения интерпретации времени.

Базовые типы зон

Внутри используются стандартные реализации:

  • FixedOffsetZone — фиксированное смещение
  • IANAZone — зона из базы IANA
  • SystemZone — системная зона окружения
  • InvalidZone — невалидная зона

Переопределение зоны при создании DateTime

DateTime.fromMillis(0, { zone: "UTC" });
DateTime.fromObject({ year: 2025 }, { zone: "Europe/Berlin" });

Каждый объект DateTime хранит свою зону, что делает экземпляры независимыми от глобальной конфигурации.

Создание пользовательской зоны

Механизм расширения поведения через зоны основан на контракте Zone. Пользовательская зона должна реализовывать интерфейс:

  • type
  • name
  • offsetName(ts)
  • formatOffset(ts, format)
  • offset(ts)
  • equals(otherZone)
  • isValid

Пример концептуальной реализации:

class CustomZone {
  constructor(offsetMinutes) {
    this._offset = offsetMinutes;
  }

  get type() {
    return "custom";
  }

  get name() {
    return `UTC+${this._offset}`;
  }

  offset(ts) {
    return this._offset;
  }

  offsetName() {
    return this.name;
  }

  formatOffset() {
    return this.name;
  }

  equals(other) {
    return other && other.offset === this._offset;
  }

  get isValid() {
    return true;
  }
}

Использование такой зоны позволяет полностью переопределить логику интерпретации времени, включая смещение и форматирование.


Переопределение поведения через DateTime фабрики

Luxon не позволяет изменять внутреннюю реализацию DateTime, но предоставляет множество точек контроля через фабричные методы.

fromMillis и fromSeconds

DateTime.fromMillis(1620000000000, { zone: "UTC" });
DateTime.fromSeconds(1620000000, { zone: "local" });

Эти методы определяют:

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

fromObject

Наиболее гибкий способ создания даты:

DateTime.fromObject(
  { year: 2025, month: 1, day: 1, hour: 10 },
  { zone: "Europe/Moscow", locale: "ru" }
);

Здесь переопределяется сразу несколько аспектов поведения:

  • календарная система
  • часовой пояс
  • локализация

Локальное переопределение поведения экземпляров

Каждый DateTime является неизменяемым, но позволяет создавать производные объекты с изменённым поведением.

setZone

dt.setZone("Asia/Tokyo");

Меняется только интерпретация времени, без изменения абсолютного момента.

setLocale

dt.setLocale("en");

Изменяет форматирование без влияния на значение времени.

reconfiguration через set

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

dt.set({ zone: "UTC", locale: "fr" });

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

Поведение форматирования зависит от Intl API и внутренних правил Luxon.

toFormat

dt.toFormat("yyyy LLL dd");

Формат зависит от:

  • локали
  • календаря
  • зоны

Изменение Settings.defaultLocale или instance locale приводит к изменению результата без изменения кода форматирования.

toLocaleString

dt.toLocaleString(DateTime.DATETIME_FULL);

Поведение этого метода полностью зависит от Intl, но может быть переопределено через глобальные настройки среды выполнения (Intl.NumberFormat, Intl.DateTimeFormat).


Переопределение системного времени

Ключевой механизм тестируемости — Settings.now.

Симуляция времени

Settings.now = () => Date.UTC(2030, 0, 1);

Любые вызовы:

  • DateTime.now()
  • DateTime.local()

будут возвращать фиксированную точку отсчёта.

Это влияет на:

  • вычисления diff
  • сравнения дат
  • интервалы

Поведение относительных вычислений

Методы diff и plus/minus зависят от текущей конфигурации зоны и локали.

dt.plus({ days: 1 });
dt.minus({ hours: 5 });

Переопределение зоны влияет на результат, поскольку:

  • сутки могут содержать 23/24/25 часов при DST
  • переходы времени учитываются автоматически

Переопределение календарной системы

Luxon поддерживает альтернативные календари через Intl (частично).

Settings.defaultOutputCalendar = "islamic";

Это влияет на:

  • отображение даты
  • форматирование
  • локализацию

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


Поведение через наследование логики (обёртки)

Прямое наследование DateTime не предусмотрено как основной путь расширения, поэтому используется композиция.

Обёртка над DateTime

function CustomDateTime(dt) {
  return {
    original: dt,
    toString() {
      return `[CUSTOM] ${dt.toISO()}`;
    }
  };
}

Такой подход позволяет:

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

Влияние настроек на цепочки операций

Все цепочки DateTime наследуют текущие Settings в момент создания экземпляра.

Settings.defaultZone = "UTC";

const a = DateTime.now();
Settings.defaultZone = "Asia/Tokyo";

const b = DateTime.now();
  • a использует UTC
  • b использует Asia/Tokyo

Это демонстрирует, что поведение фиксируется на момент инстанцирования, а не вычисляется динамически.


Конфигурационное разделение контекстов

В сложных приложениях поведение Luxon часто разделяется на контексты:

  • системный контекст (UTC, сервер)
  • пользовательский контекст (locale, zone)
  • отчётный контекст (фиксированное время)

Luxon позволяет моделировать такие сценарии через:

  • Settings
  • zone overrides
  • локальные параметры DateTime

Каждый уровень переопределяет поведение независимо, формируя многослойную модель управления временем.