Работа с датами в Luxon начинается с преобразования входных значений
в экземпляры DateTime. Любое значение, поступающее извне
системы — строка пользователя, ответ API, данные базы — рассматривается
как потенциально некорректное.
Ключевой принцип заключается в том, что создание
DateTime не гарантирует корректный результат. Даже при
отсутствии исключений объект может оказаться невалидным.
import { DateTime } from "luxon";
const dt = DateTime.fromISO("невалидная строка");
console.log(dt.isValid); // false
console.log(dt.invalidReason); // "unparsable"
Защитное программирование в этом контексте требует обязательной
проверки isValid после любого преобразования.
Повторяющаяся логика проверки приводит к дублированию кода. Более устойчивый подход — создание обёртки над парсингом:
function safeFromISO(value) {
const dt = DateTime.fromISO(value);
if (!dt.isValid) return null;
return dt;
}
Такая функция превращает Luxon-объекты в предсказуемый слой данных,
где null однозначно сигнализирует об ошибке входа.
Одна из наиболее частых причин ошибок в системах, работающих с датами, — неявное использование временной зоны окружения.
Luxon по умолчанию использует локальную зону среды выполнения. Это создаёт риск неоднозначности при обработке серверных данных.
const dt = DateTime.fromISO("2026-01-01T10:00:00");
console.log(dt.zoneName);
Защитный подход предполагает явное указание зоны:
const dt = DateTime.fromISO("2026-01-01T10:00:00", {
zone: "utc"
});
В системах с распределённой архитектурой используется стратегия унификации:
function toUtcDateTime(value) {
const dt = DateTime.fromISO(value, { zone: "utc" });
return dt.isValid ? dt : null;
}
Такой подход устраняет класс ошибок, связанных с разницей временных зон между сервисами.
Luxon не выбрасывает исключения при некорректных операциях. Вместо этого результат становится невалидным объектом.
const dt = DateTime.fromISO("invalid");
const result = dt.plus({ days: 1 });
console.log(result.isValid); // false
Это поведение требует строгого контроля цепочек вызовов.
Защитный шаблон заключается в проверке на каждом этапе:
function addDaysSafe(value, days) {
const dt = DateTime.fromISO(value);
if (!dt.isValid) return null;
const result = dt.plus({ days });
if (!result.isValid) return null;
return result;
}
Такой стиль исключает распространение ошибки по цепочке вычислений.
Метод fromFormat особенно чувствителен к несоответствиям
входных данных. Малейшее отклонение от формата приводит к невалидному
объекту.
const dt = DateTime.fromFormat("2026/01/23", "yyyy-MM-dd");
console.log(dt.isValid); // false
Защитное программирование предполагает явную фиксацию формата и проверку результата:
function parseStrict(value, format) {
const dt = DateTime.fromFormat(value, format);
return dt.isValid ? dt : null;
}
Дополнительно используется контроль локали:
DateTime.fromFormat("23.01.2026", "dd.MM.yyyy", {
locale: "ru"
});
Игнорирование локали часто приводит к скрытым ошибкам интерпретации даты.
Даты обладают естественными границами: переходы месяцев, високосные годы, переходы времени при смене часовых поясов.
Luxon корректно обрабатывает такие случаи, но результат может отличаться от ожидаемого бизнес-правила.
const dt = DateTime.fromISO("2026-01-31").plus({ months: 1 });
console.log(dt.toISO()); // 2026-02-28
Если бизнес-логика требует строгого сохранения дня месяца, необходимо вводить дополнительную проверку:
function addMonthsStrict(dt, months) {
const originalDay = dt.day;
const result = dt.plus({ months });
if (result.day !== originalDay) {
return null;
}
return result;
}
Переходы на летнее и зимнее время создают неоднозначные или несуществующие моменты времени.
const dt = DateTime.fromISO("2026-03-29T02:30:00", {
zone: "Europe/Berlin"
});
Такое время может быть автоматически скорректировано или признано невалидным в зависимости от зоны.
Для предотвращения неоднозначности применяется конвертация в UTC:
function normalizeToUtc(value, zone) {
const dt = DateTime.fromISO(value, { zone });
if (!dt.isValid) return null;
return dt.toUTC();
}
DateTime в Luxon является иммутабельным объектом. Каждый
метод возвращает новый экземпляр.
Однако защитное программирование требует учитывать возможность потери ссылки на исходное значение.
const original = DateTime.now();
const modified = original.plus({ hours: 2 });
Ошибки возникают при предположении, что original
изменяется:
original.plus({ days: 1 });
// original остаётся неизменным
Для снижения риска логических ошибок используется разделение переменных:
const baseDate = DateTime.now();
const calculatedDate = baseDate.plus({ days: 5 });
Передача DateTime между слоями системы требует
сериализации. Основной формат — ISO.
const json = dt.toISO();
Однако при обратном преобразовании необходимо учитывать возможность потери данных:
function reviveDateTime(value) {
const dt = DateTime.fromISO(value);
return dt.isValid ? dt : null;
}
При использовании JSON-десериализации дополнительно проверяется тип:
function revive(key, value) {
if (typeof value === "string" && value.includes("T")) {
const dt = DateTime.fromISO(value);
if (dt.isValid) return dt;
}
return value;
}
Duration и Interval также могут быть
невалидными при некорректных входных данных.
import { Duration } from "luxon";
const d = Duration.fromObject({ hours: "invalid" });
console.log(d.isValid); // false
function safeDuration(obj) {
const d = Duration.fromObject(obj);
return d.isValid ? d : null;
}
Для интервалов важно проверять обе границы:
import { Interval, DateTime } from "luxon";
function safeInterval(start, end) {
const s = DateTime.fromISO(start);
const e = DateTime.fromISO(end);
if (!s.isValid || !e.isValid) return null;
const interval = Interval.fromDateTimes(s, e);
return interval.isValid ? interval : null;
}
Защитное программирование в Luxon направлено на устранение недетерминированности. Основные источники нестабильности:
Для тестируемости и предсказуемости часто используется передача времени как параметра:
function calculateExpiry(now, hours) {
const dt = DateTime.fromISO(now);
if (!dt.isValid) return null;
return dt.plus({ hours });
}
Это исключает зависимость от DateTime.now() внутри
логики.
Luxon использует модель «неисключающих ошибок», где результат всегда возвращается, но может быть невалидным. Это требует дисциплины:
isValidТакая модель позволяет выстраивать устойчивые системы обработки времени без неожиданного падения выполнения, но переносит ответственность за корректность на уровень кода приложения.