Округление и обрезание

Работа с датой и временем в Luxon опирается на неизменяемые объекты DateTime и Duration, что делает операции округления и обрезания предсказуемыми и безопасными. Любая трансформация возвращает новый объект, не изменяя исходный, поэтому все операции можно выстраивать в цепочки без риска побочных эффектов.

Обрезание (truncation) до границ единиц времени

Обрезание — это приведение времени к началу или концу выбранного интервала. В Luxon для этого используются методы, которые «сбрасывают» менее значимые компоненты даты.

startOf

Метод startOf(unit) устанавливает дату в начало указанного интервала:

  • year — начало года (1 января, 00:00:00.000)
  • month — начало месяца
  • week — начало недели (зависит от локали)
  • day — начало дня (00:00:00.000)
  • hour, minute, second

Пример логики обрезания:

import { DateTime } from "luxon";

const dt = DateTime.local(2026, 5, 23, 14, 37, 52, 123);

const startOfDay = dt.startOf("day");
const startOfHour = dt.startOf("hour");
const startOfMonth = dt.startOf("month");

Каждый вызов удаляет «хвост» менее значимых единиц времени. Например, при startOf("hour") минуты, секунды и миллисекунды обнуляются.

Ключевая особенность: startOf работает детерминированно и всегда приводит значение к строго нижней границе интервала.


endOf

Метод endOf(unit) выполняет обратную операцию — устанавливает дату в последнюю возможную точку интервала:

  • конец дня: 23:59:59.999
  • конец месяца: последний день месяца в 23:59:59.999
  • конец года: 31 декабря 23:59:59.999
const endOfDay = dt.endOf("day");
const endOfMonth = dt.endOf("month");

endOf часто используется при фильтрации диапазонов, когда нужно включить весь интервал целиком.


Ручное обрезание через set

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

const truncated = dt.set({
  second: 0,
  millisecond: 0
});

Это не специализированное округление, а именно прямое присваивание значений.

Такой подход используется, когда необходимо:

  • обнулить миллисекунды перед сравнением
  • нормализовать время перед сохранением в БД
  • привести разные источники данных к единому формату точности

Обрезание Unix-времени

Luxon позволяет работать с Unix-временем через toMillis() и fromMillis(). Это удобно для «жёсткого» обрезания через математические операции.

const ms = dt.toMillis();
const truncated = DateTime.fromMillis(ms - (ms % 1000));

Здесь происходит обрезание до секунд за счёт остатка от деления.

Аналогично можно обрезать до минут:

const truncatedToMinute =
  DateTime.fromMillis(ms - (ms % 60000));

Такой способ применяется, когда требуется строгая числовая точность без использования API Luxon для компонентов.


Округление до ближайшего интервала

Luxon не предоставляет универсального метода «round()» для DateTime, поэтому округление реализуется комбинацией операций извлечения времени и математических функций.

Округление до минут

const ms = dt.toMillis();
const rounded = DateTime.fromMillis(
  Math.round(ms / 60000) * 60000
);

Логика:

  • делим время на размер интервала
  • округляем до ближайшего целого
  • возвращаем обратно в миллисекунды

Округление до часов

const roundedHour = DateTime.fromMillis(
  Math.round(ms / 3600000) * 3600000
);

Округление с учётом секунд

const roundedSecond = DateTime.fromMillis(
  Math.round(ms / 1000) * 1000
);

Округление через DateTime.diff и Duration

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

const diff = dt1.diff(dt2, ["hours", "minutes"]);

Полученный Duration можно преобразовать в числовые значения:

const hours = diff.as("hours");
const roundedHours = Math.round(hours);

Здесь округление происходит уже на уровне чисел, а не даты.


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

При работе с длительностями Luxon использует объект Duration, который может содержать «переполненные» значения (например, 90 минут вместо 1 часа 30 минут).

Метод:

duration.normalize();

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

Это не классическое округление, но важный шаг перед преобразованиями и форматированием.


Перевод длительности в нужные единицы

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

const d = diff.shiftTo("hours", "minutes", "seconds");

После этого значения можно округлять выборочно:

const hours = Math.round(d.hours);
const minutes = Math.round(d.minutes);

Такой подход полезен при отображении пользовательских таймеров и отчётов.


Точечное обнуление дробных компонентов

При работе с дробными значениями (например, результат as("seconds") может быть дробным) часто требуется принудительное усечение:

const seconds = Math.floor(diff.as("seconds"));

или округление:

const seconds = Math.round(diff.as("seconds"));

Это базовый способ устранения погрешностей вычислений времени.


Комбинированные стратегии округления

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

Приведение к границе + корректировка

const normalized = dt
  .startOf("minute")
  .plus({ minutes: Math.round(dt.second / 60) });

Округление после нормализации времени

const base = dt.startOf("day");
const offset = Math.round(dt.diff(base, "minutes").minutes);

const result = base.plus({ minutes: offset });

Обрезание при форматировании

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

const formatted = dt
  .set({ millisecond: 0 })
  .toFormat("HH:mm:ss");

Важно различать:

  • форматирование
  • фактическое изменение значения времени

Практика работы с погрешностями времени

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

const diff = dt1.diff(dt2, "seconds").seconds;

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

  • Math.floor — для отсечения будущего времени
  • Math.ceil — для гарантированного включения интервала
  • Math.round — для симметричного округления

Выбор метода зависит от логики бизнес-задачи.


Обрезание временных зон и влияние на результат

При смене временной зоны:

const shifted = dt.setZone("UTC");

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


Стабильность и предсказуемость операций

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

  • неизменяемость исходного объекта
  • отсутствие скрытых побочных эффектов
  • детерминированность результатов при одинаковом входе
  • независимость от мутабельных структур JavaScript Date

Это позволяет безопасно комбинировать несколько уровней трансформаций времени без риска накопления ошибок или смещения состояния.