Длительность интервала

Длительность в Luxon представляет собой абстракцию временного промежутка, независимого от конкретных дат и календарей. Она описывает не точку во времени, а количество времени в различных единицах: миллисекундах, секундах, минутах, часах, днях и так далее. Такой подход позволяет работать с временными интервалами как с математическими объектами, выполняя над ними преобразования, сложение, вычитание и нормализацию.

В основе лежит объект Duration, который инкапсулирует набор единиц времени и предоставляет инструменты для их преобразования и вычислений.


Создание длительности

Создание из объекта единиц

Наиболее распространённый способ создания длительности — через объект с указанием временных единиц:

import { Duration } from "luxon";

const d = Duration.fromObject({
  hours: 2,
  minutes: 30,
  seconds: 15
});

Такой объект не выполняет автоматическую нормализацию: значения сохраняются в заданных единицах, пока не применяется явное преобразование.


Создание из миллисекунд

Для работы с низкоуровневым представлением времени используется метод fromMillis:

const d = Duration.fromMillis(9000000);

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


Создание из ISO-строки

Luxon поддерживает стандарт ISO 8601 для представления длительностей:

const d = Duration.fromISO("PT2H30M15S");

Формат:

  • P — префикс периода
  • T — разделитель даты и времени
  • H, M, S — часы, минуты, секунды

Внутреннее представление длительности

Объект Duration хранит данные в виде набора единиц, например:

{
  hours: 2,
  minutes: 30,
  seconds: 15
}

При этом Luxon не всегда автоматически приводит значения к единому масштабу. Например, 90 минут не будут автоматически преобразованы в 1 час 30 минут без вызова нормализации.


Нормализация длительности

Нормализация приводит значения к стандартному виду, перераспределяя избыточные единицы:

const d = Duration.fromObject({
  hours: 1,
  minutes: 90
}).normalize();

Результат:

2 hours, 30 minutes

Метод normalize важен при вычислениях, где возможны переполнения единиц (например, секунды → минуты → часы).


Арифметика длительностей

Сложение

Длительности можно складывать:

const d1 = Duration.fromObject({ hours: 1 });
const d2 = Duration.fromObject({ minutes: 30 });

const sum = d1.plus(d2);

Результат сохраняет структуру единиц, при необходимости требуется нормализация.


Вычитание

const d1 = Duration.fromObject({ hours: 2 });
const d2 = Duration.fromObject({ minutes: 45 });

const diff = d1.minus(d2);

Результат может содержать отрицательные значения в отдельных единицах, если не выполнена нормализация.


Умножение и деление

Длительность может масштабироваться:

const d = Duration.fromObject({ minutes: 30 });

const doubled = d.mapUnits(value => value * 2);

Альтернативный способ — преобразование в миллисекунды:

const result = Duration.fromObject({ minutes: 30 })
  .as("milliseconds") * 2;

Преобразование единиц

Метод as позволяет получить длительность в конкретной единице:

const d = Duration.fromObject({
  hours: 2,
  minutes: 30
});

d.as("minutes"); // 150
d.as("seconds"); // 9000

Это преобразование учитывает все внутренние единицы и возвращает итоговое числовое значение.


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

shiftTo

Метод shiftTo позволяет перераспределить длительность в заданные единицы:

const d = Duration.fromObject({
  minutes: 150
}).shiftTo("hours", "minutes");

Результат:

2 hours, 30 minutes

Этот метод полезен при форматировании и подготовке данных для отображения.


normalize и shiftTo в сравнении

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

Форматирование длительности

toObject

Возвращает объект с текущими единицами:

const d = Duration.fromObject({
  hours: 2,
  minutes: 30
});

d.toObject();

Результат:

{ hours: 2, minutes: 30 }

toISO

Преобразование в ISO 8601:

const d = Duration.fromObject({
  hours: 2,
  minutes: 30
});

d.toISO();

Результат:

PT2H30M

Работа с отрицательными длительностями

Luxon поддерживает отрицательные интервалы времени:

const d = Duration.fromObject({ hours: -1, minutes: 30 });

Такие значения возникают при разнице между временными точками или при вычитании длительностей. При преобразованиях важно учитывать знаки всех единиц.


Преобразование между типами

Из Duration в миллисекунды

const ms = Duration.fromObject({
  minutes: 10
}).as("milliseconds");

Из миллисекунд в Duration

const d = Duration.fromMillis(600000);

Из Duration в DateTime через добавление

Хотя Duration не является моментом времени, он часто используется совместно с DateTime:

import { DateTime, Duration } from "luxon";

const start = DateTime.now();
const duration = Duration.fromObject({ hours: 3 });

const end = start.plus(duration);

Масштабирование единиц

Метод mapUnits позволяет трансформировать все единицы сразу:

const d = Duration.fromObject({
  hours: 1,
  minutes: 30
});

const scaled = d.mapUnits(x => x * 2);

Результат:

2 hours, 60 minutes

Без нормализации значения остаются в исходных единицах.


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

Объект Duration может быть невалидным при некорректных входных данных:

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

d.isValid; // false

Ошибочные значения могут возникать при некорректных вычислениях или преобразованиях.


Интероперабельность с Interval

Хотя Interval относится к другому типу Luxon, Duration часто используется для вычисления его длины:

import { Interval } from "luxon";

const interval = Interval.fromDateTimes(
  DateTime.now(),
  DateTime.now().plus({ hours: 5 })
);

const duration = interval.toDuration();

Особенности работы с единицами

Luxon поддерживает множество единиц:

  • years
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds
  • milliseconds

При этом важно учитывать, что месяцы и годы не фиксированы по длительности, что влияет на точность вычислений.


Сравнение длительностей

Для сравнения используется приведение к общей единице:

const d1 = Duration.fromObject({ minutes: 90 });
const d2 = Duration.fromObject({ hours: 1 });

d1.as("minutes") > d2.as("minutes");

Иммутабельность

Все операции с Duration возвращают новый объект. Исходная длительность не изменяется:

const d1 = Duration.fromObject({ minutes: 30 });
const d2 = d1.plus({ minutes: 10 });

// d1 остаётся неизменным

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

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

const step = Duration.fromObject({ seconds: 5 });

const sequence = Array.from({ length: 5 }, (_, i) =>
  step.mapUnits(v => v * i).as("seconds")
);

Такой подход позволяет моделировать равномерные интервалы, таймеры и расписания без привязки к конкретным датам.