В Luxon Duration представляет собой структуру для работы с длительностями времени, которая отличается от абсолютных моментов времени (DateTime) тем, что оперирует относительными единицами: годами, месяцами, днями, часами, минутами, секундами и миллисекундами. Ключевая сложность при работе с Duration заключается в неоднородности единиц измерения и их неоднозначной взаимной конвертации.
Внутреннее представление Duration в Luxon основано на наборе полей:
Эти единицы не являются взаимно однозначно конвертируемыми. Например, месяц может содержать 28, 29, 30 или 31 день, а год — 365 или 366 дней. Из-за этого Duration хранит значения раздельно, не приводя их автоматически к единому масштабу.
Проблема возникает при необходимости:
Для этого используется нормализация.
Нормализация Duration — это процесс приведения разрозненных единиц к согласованному виду по правилам Luxon. Основная идея заключается в перераспределении значений между полями так, чтобы:
Важный момент: нормализация не означает преобразование всего Duration в одну единицу (например, в миллисекунды). Это именно структурное выравнивание полей.
Метод normalize() выполняет приведение Duration к каноническому виду, перераспределяя значения между полями в пределах их естественных диапазонов.
Пример базового поведения:
import { Duration } from "luxon";
const d = Duration.fromObject({
hours: 1,
minutes: 120,
seconds: 90
});
const normalized = d.normalize();
После нормализации:
Итоговая структура становится:
{
hours: 3,
minutes: 1,
seconds: 30
}
normalize() не изменяет смысл Duration, но приводит его к компактному и согласованному виду.
Нормализация в Luxon выполняется по каскадному принципу:
Однако важно учитывать: переходы через месяцы и годы не всегда выполняются линейно, поскольку длина месяца и года зависит от конкретной даты, если она задана в контексте DateTime.
Duration не всегда может быть полностью нормализован в строгом математическом смысле. Например:
Поэтому Luxon разделяет:
Календарная нормализация требует привязки к конкретной временной точке.
Метод shiftTo выполняет более агрессивную трансформацию: он приводит Duration к заданному набору единиц, перераспределяя всё в указанные поля.
const d = Duration.fromObject({
hours: 2,
minutes: 120,
seconds: 90
});
const shifted = d.shiftTo("hours", "minutes", "seconds");
В отличие от normalize():
Если normalize — это «очистка», то shiftTo — «пересборка».
Метод rescale применяется для приведения Duration к минимальному набору единиц без избыточных полей.
const d = Duration.fromObject({
hours: 1,
minutes: 120,
seconds: 90
});
const rescaled = d.rescale();
После rescale результат может быть представлен, например, так:
{
hours: 3,
minutes: 1,
seconds: 30
}
Отличие от normalize заключается в том, что rescale стремится максимально «сжать» представление, устраняя избыточные единицы.
Duration может содержать отрицательные значения:
const d = Duration.fromObject({
minutes: -90,
seconds: -30
});
При нормализации возникает необходимость согласованного переноса отрицательных значений между единицами. Luxon обрабатывает такие случаи по правилам:
Результатом становится структурно корректная отрицательная длительность, а не набор разрозненных отрицательных полей.
При работе с миллисекундами и преобразованиями между единицами может возникать накопление ошибок IEEE 754:
Нормализация частично компенсирует такие эффекты, перераспределяя остатки в младшие единицы, но не устраняет фундаментальные ограничения числового представления.
Метод toHuman() использует нормализованное представление Duration для формирования строки:
Duration.fromObject({
minutes: 90
}).normalize().toHuman();
Без нормализации результат может быть менее предсказуемым, поскольку поля могут содержать переполненные значения.
Сравнение двух Duration без предварительной нормализации может давать некорректные результаты:
const a = Duration.fromObject({ hours: 1, minutes: 60 });
const b = Duration.fromObject({ hours: 2 });
a.equals(b); // может быть false
После normalize:
a.normalize().equals(b.normalize()); // true
Причина в том, что внутреннее представление не гарантирует каноническую форму.
Нормализация становится критичной в следующих задачах:
Особенно важна нормализация при смешанных единицах, где пользовательские данные могут поступать в произвольной форме.
При наличии месяцев и лет нормализация зависит от контекста DateTime:
Это ограничение принципиально связано с календарной природой времени.
Normalize выполняет функцию приведения Duration к стабильному внутреннему виду, пригодному для:
Без нормализации Duration остаётся «сырой» структурой, чувствительной к способу конструирования и порядку операций.