Поведение методов на невалидных объектах

В библиотеке Luxon ключевая концепция обработки ошибок в датах основана не на выбросе исключений, а на распространении состояния невалидности через цепочку вычислений.

Объект считается невалидным, если его невозможно корректно интерпретировать как дату/время или интервал. В таком случае он получает внутренний флаг состояния isValid = false и сопровождающее описание причины в invalidReason.

Типичные причины появления невалидного объекта:

  • некорректная строка даты (fromISO, fromFormat)
  • выход значений за допустимые диапазоны
  • ошибки парсинга таймзоны
  • некорректные арифметические операции над датами
  • повреждённые или неполные данные при конструировании

Базовое поведение методов при невалидных объектах

Принцип распространения невалидности

Ключевое правило: если объект невалиден, все производные операции возвращают невалидный результат

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

Пример:

import { DateTime } from "luxon";

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

console.log(dt.isValid); // false

const updated = dt.plus({ days: 5 });

console.log(updated.isValid); // false

Метод plus() не пытается “исправить” исходное значение — он лишь сохраняет цепочку невалидности.


Поведение арифметических методов

plus и minus

Методы plus() и minus() полностью игнорируют попытки нормализации данных, если исходный объект невалиден.

const dt = DateTime.fromISO("not-a-date");

const result = dt.plus({ hours: 3 });

console.log(result.isValid); // false

Если же объект валиден, но переданы некорректные аргументы, результат также становится невалидным:

const dt = DateTime.now();

const result = dt.plus({ hours: "abc" });

console.log(result.isValid); // false

startOf и endOf

Методы, изменяющие точность даты, также подчиняются правилу распространения:

const dt = DateTime.invalid("custom reason");

console.log(dt.startOf("day").isValid); // false

Форматирование невалидных значений

toISO, toString и аналогичные методы

При попытке форматирования невалидного объекта возвращается строка-индикатор:

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

console.log(dt.toISO()); 
// null или "Invalid DateTime" (в зависимости от метода)

Типичное поведение:

  • toISO()null
  • toString()"Invalid DateTime"
  • toFormat()"Invalid DateTime"

toFormat

Метод toFormat не пытается интерпретировать данные:

const dt = DateTime.invalid("parse error");

console.log(dt.toFormat("yyyy LLL dd"));
// Invalid DateTime

Форматирование не выполняется даже частично.


Доступ к свойствам

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

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

console.log(dt.year); // NaN или undefined (зависит от контекста)

Однако гарантированное поведение:

  • isValid всегда false
  • invalidReason содержит причину
  • invalidExplanation может содержать расширенное описание
console.log(dt.invalidReason); 
// "unparsable" / "invalid input" / "unsupported zone" и т.д.

Поведение сравнений

Сравнение невалидных объектов

Любые операции сравнения с невалидными объектами дают предсказуемо отрицательный результат:

const a = DateTime.invalid("a");
const b = DateTime.now();

console.log(a < b); // false
console.log(a > b); // false

При этом сравнение двух невалидных объектов также не даёт значимого результата:

const a = DateTime.invalid("a");
const b = DateTime.invalid("b");

console.log(a.equals(b)); // false

Метод equals() требует валидности обоих объектов.


Поведение equals и isEqual

Метод equals() строго проверяет корректность:

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

console.log(dt.equals(DateTime.now())); // false

Если один из объектов невалиден — результат всегда false.


Поведение toJSDate

При попытке конвертации в нативный объект Jav * aScript:

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

console.log(dt.toJSDate());
// Invalid Date (Date object)

Возвращается объект Date, но содержащий значение Invalid Date. Это важно:

  • тип сохраняется
  • значение становится неиспользуемым

Поведение Duration при невалидности

Аналогичный механизм распространяется на Duration.

import { Duration } from "luxon";

const d = Duration.fromObject({ hours: "x" });

console.log(d.isValid); // false

Любые операции:

const d2 = d.plus({ minutes: 10 });
console.log(d2.isValid); // false

Поведение Interval при невалидных границах

Interval становится невалидным, если хотя бы одна граница некорректна:

import { Interval, DateTime } from "luxon";

const start = DateTime.invalid("bad");
const end = DateTime.now();

const interval = Interval.fromDateTimes(start, end);

console.log(interval.isValid); // false

Все методы интервала наследуют это состояние:

console.log(interval.length()); // NaN
console.log(interval.toISO());  // null или "Invalid Interval"

Логика цепочек вызовов

Одно из ключевых свойств Luxon — неизменяемость объектов. В связке с невалидностью это приводит к строгому правилу:

один невалидный шаг делает всю цепочку невалидной

const result = DateTime
  .fromISO("bad-date")
  .setZone("UTC")
  .plus({ days: 1 })
  .toFormat("yyyy-MM-dd");

console.log(result);
// Invalid DateTime

Даже если последующие операции корректны, они не выполняются.


Методы проверки состояния

isValid

Основной индикатор состояния:

const dt = DateTime.fromISO("2020-01-01");

console.log(dt.isValid); // true

invalidReason

Код причины:

  • unparsable
  • invalid input
  • unsupported zone
  • out of range
  • conflicting configuration
const dt = DateTime.fromISO("2020-99-99");

console.log(dt.invalidReason);

invalidExplanation

Человекочитаемое описание:

console.log(dt.invalidExplanation);
// более подробное объяснение ошибки

Поведение при сериализации

При преобразовании в JSON:

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

console.log(JSON.stringify(dt));

Результат обычно:

  • null
  • либо объект с полем ошибки (в зависимости от реализации и версии)

Важно: сериализация не “исправляет” данные.


Особенности поведения при локализации

Локализованные методы (toLocaleString, toLocaleParts) также подчиняются правилу невалидности:

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

console.log(dt.toLocaleString());
// "Invalid DateTime"

Общая модель распространения ошибок

Поведение невалидных объектов в Luxon строится на трёх принципах:

  1. Отсутствие исключений при обработке
  2. Иммутабельность состояния невалидности
  3. Полная блокировка вычислений при ошибке

Это обеспечивает предсказуемость:

  • ошибка не “всплывает” неожиданно
  • ошибка не исчезает случайно
  • ошибка распространяется до точки проверки isValid

Типичные ошибки использования

Игнорирование isValid

const dt = DateTime.fromISO(userInput);

console.log(dt.toFormat("yyyy")); // риск "Invalid DateTime"

Цепочки без проверки

const result = DateTime.fromISO(input)
  .plus({ days: 1 })
  .toISO();

При некорректном input результат будет null или строкой ошибки.


Предположение о частичной валидности

Luxon не поддерживает “частично корректные” даты:

  • либо объект полностью валиден
  • либо полностью невалиден

Итоговая модель поведения

Поведение методов на невалидных объектах в Luxon сводится к единому правилу:

любой метод, получивший невалидный объект, возвращает невалидный объект того же типа без попытки исправления данных

Это делает систему детерминированной и упрощает построение цепочек вычислений без скрытых преобразований и неожиданных побочных эффектов.