В библиотеке Luxon длительности представлены объектом
Duration, который позволяет работать с промежутками времени
как с полноценными значениями: складывать, вычитать, масштабировать,
нормализовать и преобразовывать между единицами измерения. В отличие от
простых числовых представлений времени, Duration учитывает
сложность календарных единиц (дни, месяцы, годы), а также различие между
фиксированными (секунды, минуты) и переменными (месяцы) интервалами.
Перед выполнением операций длительность должна быть создана в одном из поддерживаемых форматов.
import { Duration } from "luxon";
const d1 = Duration.fromObject({
hours: 2,
minutes: 30
});
const d2 = Duration.fromObject({
hours: 1,
minutes: 45
});
Также возможны альтернативные способы:
Duration.fromMillis(3600000); // 1 час
Duration.fromISO("PT2H30M"); // ISO 8601
Ключевая особенность: Luxon хранит внутреннее представление в виде набора единиц, а не одного числа.
Операция сложения выполняется методом plus. Он
возвращает новый объект Duration, не изменяя исходный.
const d1 = Duration.fromObject({ hours: 2, minutes: 30 });
const d2 = Duration.fromObject({ hours: 1, minutes: 45 });
const result = d1.plus(d2);
Результат:
Важно учитывать, что Luxon не всегда автоматически нормализует единицы до одного масштаба, если они заданы в разных формах.
Пример с разными единицами:
const d1 = Duration.fromObject({ minutes: 90 });
const d2 = Duration.fromObject({ hours: 1 });
const result = d1.plus(d2);
Здесь результат может остаться в смешанном виде, если не применить нормализацию.
Метод minus выполняет обратную операцию сложения.
const d1 = Duration.fromObject({ hours: 5 });
const d2 = Duration.fromObject({ hours: 2, minutes: 30 });
const result = d1.minus(d2);
Результат:
Если вычитание приводит к отрицательным значениям, Luxon сохраняет знак на уровне всей длительности:
const d1 = Duration.fromObject({ hours: 1 });
const d2 = Duration.fromObject({ hours: 3 });
const result = d1.minus(d2);
Результат будет отрицательной длительностью.
Метод mapUnits отсутствует для умножения, а
масштабирование выполняется через mapUnits (для
трансформации единиц) или через shiftTo + as +
ручное умножение. Однако стандартный способ — использовать
mapUnits для преобразований и multiply через
сторонние операции:
Фактически Luxon предоставляет scale через
mapUnits и ручное управление, но чаще используют:
const d = Duration.fromObject({ minutes: 30 });
const scaled = d.mapUnits(value => value * 2);
Результат: 60 минут.
const d = Duration.fromObject({ hours: 2 });
const halved = d.mapUnits(value => value / 2);
Результат:
Метод normalize приводит длительность к более
“естественному” виду, перераспределяя единицы.
const d = Duration.fromObject({
minutes: 120,
seconds: 90
});
const normalized = d.normalize();
Результат:
Нормализация особенно важна после арифметических операций, когда значения выходят за стандартные пределы (например, 90 минут).
Метод shiftTo позволяет преобразовать длительность в
заданные единицы измерения.
const d = Duration.fromObject({ minutes: 150 });
const result = d.shiftTo("hours", "minutes");
Результат:
Особенность: Luxon распределяет значения между указанными единицами, сохраняя точность.
Метод as возвращает числовое значение длительности в
выбранной единице.
const d = Duration.fromObject({
hours: 2,
minutes: 30
});
d.as("minutes"); // 150
d.as("hours"); // 2.5
Этот метод используется для сравнения и числовых вычислений.
Luxon сохраняет знак длительности и корректно обрабатывает операции с отрицательными значениями.
const d1 = Duration.fromObject({ hours: 1 });
const d2 = Duration.fromObject({ hours: 3 });
const result = d1.minus(d2);
Результат эквивалентен:
Можно явно инвертировать знак:
const neg = result.negate();
Luxon позволяет выполнять операции над смешанными единицами без предварительной конвертации.
const d1 = Duration.fromObject({
days: 1,
hours: 5
});
const d2 = Duration.fromObject({
hours: 20
});
const result = d1.plus(d2);
Результат:
Особенность Luxon заключается в том, что месяцы и годы не имеют фиксированной длины. Поэтому арифметика с ними не всегда линейна.
const d1 = Duration.fromObject({ months: 1 });
const d2 = Duration.fromObject({ days: 30 });
const result = d1.plus(d2);
Здесь результат может оставаться в раздельных единицах, поскольку:
После сложения и вычитания часто требуется привести результат к согласованному виду.
const d1 = Duration.fromObject({ minutes: 90 });
const d2 = Duration.fromObject({ minutes: 45 });
const result = d1.plus(d2).normalize();
Результат:
Для работы с отдельными компонентами используются get и
set.
const d = Duration.fromObject({ hours: 2, minutes: 30 });
d.get("hours"); // 2
Изменение:
const upd ated = d.se t({ minutes: 45 });
Это создаёт новый объект длительности.
Luxon позволяет переводить длительность в абсолютное число миллисекунд.
const d = Duration.fromObject({ seconds: 90 });
d.toMillis(); // 90000
Обратное преобразование:
Duration.fromMillis(90000);
Так как Duration не всегда сравнивается напрямую как число, используется преобразование:
const d1 = Duration.fromObject({ minutes: 90 });
const d2 = Duration.fromObject({ hours: 1 });
d1.as("minutes") > d2.as("minutes"); // true
Luxon позволяет строить цепочки преобразований:
const result = Duration.fromObject({ minutes: 60 })
.plus(Duration.fromObject({ minutes: 30 }))
.minus(Duration.fromObject({ minutes: 10 }))
.shiftTo("hours", "minutes")
.normalize();
Результат:
as используется для числовых вычисленийshiftTo используется для перераспределения единицПри масштабировании и делении возможны дробные значения:
const d = Duration.fromObject({ minutes: 1 });
const result = d.mapUnits(v => v / 3);
Результат:
Для устранения накопления погрешностей используется
normalize или последующее округление через
as.
Типичный рабочий сценарий включает несколько этапов:
const work = Duration.fromObject({ hours: 8 });
const breakTime = Duration.fromObject({ minutes: 30 });
const total = work
.minus(breakTime)
.plus(Duration.fromObject({ minutes: 15 }))
.shiftTo("hours", "minutes")
.normalize();
Итоговая длительность отражает реальное рабочее время после корректировок.