Разница между датами

В библиотеке Luxon работа с разницей между датами строится на двух ключевых сущностях: Duration и Interval. Первая используется для представления продолжительности, вторая — для описания промежутка между двумя моментами времени как диапазона. Основной инструмент вычисления разницы — метод diff, возвращающий объект Duration, который позволяет оперировать разницей в различных единицах измерения.


Базовое вычисление разницы: DateTime.diff

Метод diff применяется к объекту DateTime и принимает другой момент времени в качестве аргумента. Результатом является объект Duration.

import { DateTime } from "luxon";

const start = DateTime.fromISO("2024-01-01T00:00:00");
const end = DateTime.fromISO("2024-01-10T12:00:00");

const diff = end.diff(start);

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

diff.toObject();
// { milliseconds: 820800000 }

Разница в заданных единицах измерения

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

const diffDays = end.diff(start, "days");
diffDays.toObject();
// { days: 9.5 }

Поддерживаются следующие базовые единицы:

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

Множественные единицы

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

const diffComplex = end.diff(start, ["days", "hours", "minutes"]);

diffComplex.toObject();
// {
//   days: 9,
//   hours: 12,
//   minutes: 0
// }

В этом случае Luxon выполняет разложение разницы по указанным компонентам.


Округление и точность результата

Duration в Luxon хранит дробные значения, но может быть приведён к целым значениям с помощью методов округления.

const diff = end.diff(start, "days");

diff.as("days"); // 9.5
diff.toObject(); // { days: 9.5 }

Для округления используются методы:

  • shiftTo() — перераспределение единиц
  • normalize() — нормализация значений
  • toFormat() — форматированное представление

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

const d = end.diff(start, ["days", "hours"]).normalize();

Разница относительно текущего времени: diffNow

Метод diffNow вычисляет разницу между указанной датой и текущим моментом времени.

const future = DateTime.fromISO("2025-01-01");

const diff = future.diffNow("days");

Если дата находится в будущем, результат будет положительным. Если в прошлом — отрицательным.

const past = DateTime.fromISO("2020-01-01");

past.diffNow("days").toObject();
// отрицательное значение

Отрицательные значения разницы

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

end.diff(start); // положительное значение
start.diff(end); // отрицательное значение

Это важно при построении логики временных интервалов, где направление имеет смысл (например, обратный отсчёт).


Приведение разницы к нужной единице: as()

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

const diff = end.diff(start);

diff.as("hours"); // 228
diff.as("days");  // 9.5

Метод as полезен для расчётов, где требуется одно числовое значение.


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

При работе с несколькими единицами возникает ситуация, когда значения выходят за логические пределы (например, 90 минут). Luxon предоставляет механизм нормализации:

const d = end.diff(start, ["hours", "minutes"]).normalize();

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

  • 90 минут → 1 час 30 минут
  • 25 часов → 1 день 1 час

Сравнение diff и Interval

Хотя diff возвращает Duration, для анализа временного промежутка как диапазона используется Interval.

import { Interval } from "luxon";

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

Interval предоставляет дополнительные возможности:

  • проверка пересечений
  • объединение интервалов
  • измерение длины

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

interval.length("days");

Пересечение интервалов

Interval позволяет определять пересечения временных промежутков:

const a = Interval.fromDateTimes(
  DateTime.fromISO("2024-01-01"),
  DateTime.fromISO("2024-01-10")
);

const b = Interval.fromDateTimes(
  DateTime.fromISO("2024-01-05"),
  DateTime.fromISO("2024-01-15")
);

a.overlaps(b); // true

Получение общей длительности через Interval

Interval позволяет более естественно работать с диапазонами времени:

const duration = interval.toDuration("days");
duration.toObject(); // { days: 9 }

В отличие от diff, Interval ориентирован на работу с диапазоном как с сущностью, а не просто разницей двух точек.


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

Особенность Luxon заключается в различии между:

  • фиксированными единицами (hours, minutes, seconds)
  • календарными единицами (months, years)

Календарные единицы зависят от календарной системы и длины месяцев.

const a = DateTime.fromISO("2024-01-01");
const b = DateTime.fromISO("2025-01-01");

b.diff(a, "months").toObject();
// { months: 12 }

Но при более сложных интервалах возможны дробные значения:

const a = DateTime.fromISO("2024-01-31");
const b = DateTime.fromISO("2024-02-29");

b.diff(a, "months").toObject();
// значение может быть дробным

Разница с учётом часовых поясов

DateTime в Luxon всегда учитывает временную зону. Разница между датами может изменяться при переходе между зонами.

const a = DateTime.fromISO("2024-01-01T10:00", { zone: "UTC" });
const b = DateTime.fromISO("2024-01-01T10:00", { zone: "Europe/Berlin" });

b.diff(a, "hours").toObject();

Такой расчёт учитывает смещение UTC и может давать неожиданные результаты при игнорировании зоны.


Использование diff для сложных вычислений

Разложение времени на компоненты

const diff = end.diff(start, ["years", "months", "days", "hours"]);

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

  • возраст
  • время до события
  • длительность процессов

Практическая модель расчёта длительности

Часто применяется комбинация:

const duration = end.diff(start).shiftTo("days", "hours", "minutes");

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


Особенности точности

Luxon опирается на миллисекундную точность. При работе с большими диапазонами возможны:

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

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

Подсчёт времени до события

const event = DateTime.fromISO("2026-12-31");
const daysLeft = event.diffNow("days").toObject();

Определение возраста

const birth = DateTime.fromISO("1990-05-10");
const age = DateTime.now().diff(birth, "years").toObject();

Анализ длительности процессов

const start = DateTime.now();
// выполнение операции
const end = DateTime.now();

const duration = end.diff(start, "milliseconds");

Итоговая модель работы разницы дат в Luxon

Внутренняя модель Luxon строится на трёх уровнях:

  1. DateTime — точки во времени
  2. Duration — разница между точками
  3. Interval — диапазон между точками

Метод diff формирует Duration, который может быть преобразован, нормализован и представлен в различных единицах, обеспечивая гибкость при работе с временными вычислениями различной сложности.