Единицы измерения

В библиотеке Luxon работа со временем строится вокруг строго определённой системы единиц, в которой базовыми сущностями выступают календарные и абсолютные интервалы. Поддерживаются как фиксированные единицы времени (миллисекунды, секунды, минуты, часы), так и календарные (дни, недели, месяцы, годы), поведение которых зависит от контекста календаря и временной зоны.

Базовая система единиц

Luxon оперирует следующими основными единицами:

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

Эти единицы применяются в объектах типа Duration, а также при арифметике дат через DateTime.

Ключевая особенность системы заключается в разделении:

  • фиксированные единицы (milliseconds, seconds, minutes, hours) имеют строго определённую длительность;
  • календарные единицы (days, weeks, months, years) зависят от контекста даты и не имеют фиксированного перевода в миллисекунды.

Duration и единицы измерения

Объект Duration представляет длительность, выраженную в одной или нескольких единицах.

Создание через объектную форму:

import { Duration } from "luxon";

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

Внутренне Luxon хранит каждую единицу отдельно, не сводя всё к одной шкале до момента явной конвертации.

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

При необходимости Luxon может привести разнородные единицы к согласованному виду:

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

Нормализация перераспределяет значения между единицами, сохраняя корректную иерархию (например, 120 минут превращаются в 2 часа).


Конвертация между единицами

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

Метод as выполняет пересчёт длительности в одну выбранную единицу:

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

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

Важно учитывать, что результат зависит от типа единиц:

  • фиксированные единицы дают точный результат;
  • календарные могут давать приближения.

Получение объекта с пересчитанными значениями

d.shiftTo("hours", "minutes").toObject();

shiftTo перераспределяет длительность в указанные единицы, сохраняя общую величину.

Пример поведения:

  • исходно: 90 minutes

  • после shiftTo(“hours”, “minutes”):

    • 1 hour
    • 30 minutes

Календарные единицы и их особенности

Календарные единицы обладают переменной длиной:

  • месяц может содержать 28–31 день;
  • год может содержать 365 или 366 дней;
  • неделя зависит от календарной системы.

Из-за этого Luxon не выполняет прямого преобразования:

Duration.fromObject({ months: 1 }).as("days");

Результат зависит от контекста, если он задан через дату, иначе используется усреднённая модель.


Работа с DateTime и единицами

DateTime поддерживает арифметику с использованием Duration.

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

import { DateTime, Duration } from "luxon";

const dt = DateTime.local();
const updated = dt.plus({ days: 3, hours: 5 });

Каждая единица интерпретируется согласно календарю конкретного момента времени.


Вычитание длительности

const earlier = dt.minus({
  weeks: 2,
  minutes: 15
});

Календарные единицы учитывают особенности временной зоны и переходов (например, DST).


Интервалы и единицы измерения

Объект Interval описывает промежуток между двумя точками во времени.

import { Interval, DateTime } from "luxon";

const start = DateTime.local();
const end = start.plus({ hours: 4 });

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

Длина интервала

interval.length("hours"); // 4

Поддерживаются те же единицы, что и в Duration, но интервал всегда вычисляется между двумя моментами времени.


ISO-длительности и стандарт представления

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

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

Структура ISO:

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

Также поддерживается преобразование обратно:

d.toISO(); // "PT2H30M"

Представление и форматирование единиц

Объектное представление

d.toObject();

Возвращает разложение по единицам:

{
  hours: 2,
  minutes: 30
}

Человекочитаемый формат

d.toHuman();

Формирует строку на основе локали и единиц:

  • “2 hours, 30 minutes”
  • локализованные варианты зависят от настроек среды

Точность и дробные значения

Luxon допускает дробные значения в длительностях:

Duration.fromObject({
  hours: 1.5
});

Внутренне такие значения могут нормализоваться:

  • 1.5 hours → 1 hour 30 minutes

При конвертации дробные части перераспределяются в младшие единицы.


Сравнение единиц

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

const a = Duration.fromObject({ hours: 2 });
const b = Duration.fromObject({ minutes: 90 });

a.equals(b); // false без нормализации

После нормализации:

a.normalize().equals(b.normalize()); // true

Влияние временных зон на единицы

При использовании DateTime единицы интерпретируются в контексте зоны:

  • добавление дней может пересекать переходы времени;
  • часы могут изменяться при переходе на летнее/зимнее время.

Пример:

dt.setZone("Europe/Berlin").plus({ days: 1 });

Фактическая длительность в часах может отличаться от 24.


Особенности вычислений с месяцами и годами

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

DateTime.local(2024, 1, 31).plus({ months: 1 });

Результат зависит от календарных правил:

  • январь 31 → февраль 29 (в високосный год)
  • при отсутствии соответствующего дня выполняется корректировка до последнего доступного дня месяца

Внутреннее разделение единиц

Luxon разделяет единицы на два класса:

  1. Fixed units

    • milliseconds
    • seconds
    • minutes
    • hours
  2. Calendar units

    • days
    • weeks
    • months
    • years

Такое разделение определяет стратегию вычислений:

  • fixed units конвертируются линейно;
  • calendar units зависят от контекста DateTime.

Потеря точности при преобразованиях

При смешивании единиц возможны потери точности:

Duration.fromObject({
  months: 1,
  days: 15
}).as("days");

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


Каноническая форма длительности

Luxon может приводить длительность к каноническому виду:

Duration.fromObject({
  minutes: 120
}).toObject();

После нормализации:

  • 2 hours
  • 0 minutes (в зависимости от вызова normalize)

Канонизация обеспечивает минимальное число активных единиц.