Метод endOf

Метод endOf в библиотеке Luxon используется для приведения объекта DateTime к последнему моменту заданного временного интервала. Он позволяет быстро получить «конец» года, месяца, дня, часа и других временных единиц, сохраняя при этом неизменяемость исходного объекта.

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


Общая сигнатура и поведение

Метод вызывается на экземпляре DateTime:

DateTime.endOf(unit: string): DateTime
  • unit — строка, определяющая временную единицу ("year", "month", "day", "hour", "minute", "second", "millisecond").
  • Возвращается новый объект DateTime, так как Luxon строго придерживается неизменяемости (immutability).

Исходный объект не изменяется.


Логика работы по уровням точности

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

Год

dt.endOf("year")

Результат:

  • 31 декабря 23:59:59.999 (в локальной или заданной зоне времени)

Год полностью «закрывается» до последнего возможного момента.


Месяц

dt.endOf("month")

Результат:

  • Последний день месяца
  • 23:59:59.999

Количество дней учитывается автоматически, включая високосные годы и месяцы с 30/31 днем.


День

dt.endOf("day")

Результат:

  • 23:59:59.999 текущего дня

Используется для построения дневных диапазонов.


Час

dt.endOf("hour")

Результат:

  • XX:59:59.999

Минуты, секунды и миллисекунды устанавливаются в максимальные значения.


Минута

dt.endOf("minute")

Результат:

  • XX:YY:59.999

Секунда

dt.endOf("second")

Результат:

  • XX:YY:ZZ.999

Миллисекунда

dt.endOf("millisecond")

Фактически возвращает тот же момент, так как более мелкой единицы нет.


Взаимосвязь с startOf

Метод endOf часто используется совместно с startOf для построения диапазонов.

const start = dt.startOf("month");
const end = dt.endOf("month");

Такой подход позволяет формировать точные интервалы:

  • начало месяца
  • конец месяца

Это базовый паттерн для фильтрации данных по диапазону дат.


Практическое использование в диапазонах

Фильтрация событий

При работе с массивами дат:

const start = DateTime.now().startOf("day");
const end = DateTime.now().endOf("day");

events.filter(e => {
  const time = DateTime.fromISO(e.date);
  return time >= start && time <= end;
});

Метод endOf("day") гарантирует включение всех событий до последней миллисекунды суток.


Построение отчетных периодов

const monthStart = dt.startOf("month");
const monthEnd = dt.endOf("month");

Используется в:

  • финансовых отчетах
  • аналитике
  • статистике посещений
  • логировании

Постраничная агрегация времени

При группировке данных по часам:

const hourStart = dt.startOf("hour");
const hourEnd = dt.endOf("hour");

Позволяет формировать временные «бакеты» для систем мониторинга.


Особенности работы с часовыми поясами

Luxon хранит информацию о временной зоне внутри объекта DateTime. Метод endOf сохраняет текущую зону без преобразования в UTC (если явно не задано).

const dt = DateTime.now().setZone("Europe/Paris");
const end = dt.endOf("day");

Результат остается в зоне Europe/Paris, но время корректируется с учетом локальных правил зоны.


Поведение при переходах времени (DST)

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

  • сутки могут быть 23 или 25 часов
  • endOf("day") всегда возвращает последний валидный момент локального дня

Luxon учитывает эти изменения автоматически через встроенный IANA timezone database.


Отличие от ручной установки времени

Эквивалентная логика без endOf выглядит значительно сложнее:

dt.set({
  hour: 23,
  minute: 59,
  second: 59,
  millisecond: 999
});

Но такой подход:

  • не учитывает календарные особенности месяца
  • требует ручной корректировки при смене единицы

endOf инкапсулирует всю логику внутри библиотеки.


Цепочки вызовов

Метод можно комбинировать с другими преобразованиями:

const result = DateTime.now()
  .plus({ days: 2 })
  .endOf("month")
  .toISO();

В цепочке важно учитывать порядок операций:

  1. арифметика даты
  2. нормализация через endOf
  3. форматирование

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

Метод часто применяется для ограничения верхней границы диапазона:

if (currentTime <= dt.endOf("day")) {
  // попадает в текущий день
}

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


Формирование границ периодов разной зернистости

День внутри месяца

const month = DateTime.now();

const range = {
  start: month.startOf("month"),
  end: month.endOf("month")
};

Час внутри дня

const hourRange = {
  start: dt.startOf("hour"),
  end: dt.endOf("hour")
};

Секунда внутри минуты

const secondRange = {
  start: dt.startOf("second"),
  end: dt.endOf("second")
};

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

Luxon работает с миллисекундной точностью. Поэтому:

  • endOf("second") всегда добавляет .999
  • более высокой точности нет
  • сравнения выполняются на уровне timestamp

Производственные сценарии

Метод активно используется в:

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

Ограничения и нюансы

  • endOf не округляет время произвольно, а строго фиксирует максимальное значение внутри единицы
  • результат всегда является новым экземпляром DateTime
  • поведение зависит от временной зоны объекта
  • при работе с UTC и локальным временем результаты будут различаться

Взаимодействие с форматированием

После применения endOf часто используется форматирование:

dt.endOf("month").toFormat("yyyy-LL-dd HH:mm:ss.SSS");

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