startOf и endOf для различных периодов

В библиотеке date-fns работа с границами временных интервалов построена вокруг набора функций startOf* и endOf*, которые позволяют получать начало и конец заданного периода на основе переданной даты. Эти функции используются при фильтрации данных по датам, построении календарей, агрегации статистики и обработке временных рядов.

Ключевая особенность подхода — иммутабельность: исходный объект Date не изменяется, каждая функция возвращает новый экземпляр.


Общая концепция startOf и endOf

Функции семейства:

  • startOfYear / endOfYear
  • startOfMonth / endOfMonth
  • startOfWeek / endOfWeek
  • startOfDay / endOfDay
  • startOfHour / endOfHour
  • startOfMinute / endOfMinute
  • startOfSecond / endOfSecond

Каждая функция:

  • обнуляет младшие единицы времени до начала периода (startOf*)
  • устанавливает максимальные значения младших единиц до конца периода (endOf*)
  • возвращает новый Date

startOfDay и endOfDay

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

startOfDay

Приводит дату к 00:00:00.000 локального времени.

import { startOfDay } from 'date-fns';

const date = new Date(2026, 0, 15, 14, 35);
const result = startOfDay(date);

console.log(result);
// 2026-01-15T00:00:00.000

endOfDay

Устанавливает время на последний момент суток: 23:59:59.999.

import { endOfDay } from 'date-fns';

const date = new Date(2026, 0, 15, 14, 35);
const result = endOfDay(date);

console.log(result);
// 2026-01-15T23:59:59.999

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

const isInDay = (date, target) => {
  const start = startOfDay(target);
  const end = endOfDay(target);
  return date >= start && date <= end;
};

startOfMonth и endOfMonth

Работа с календарными месяцами требует учета разного количества дней.

startOfMonth

import { startOfMonth } from 'date-fns';

startOfMonth(new Date(2026, 4, 17));
// 2026-05-01T00:00:00.000

endOfMonth

import { endOfMonth } from 'date-fns';

endOfMonth(new Date(2026, 4, 17));
// 2026-05-31T23:59:59.999

Особенность: функция автоматически учитывает длину месяца, включая високосные годы.


startOfYear и endOfYear

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

startOfYear

import { startOfYear } from 'date-fns';

startOfYear(new Date(2026, 7, 10));
// 2026-01-01T00:00:00.000

endOfYear

import { endOfYear } from 'date-fns';

endOfYear(new Date(2026, 7, 10));
// 2026-12-31T23:59:59.999

startOfWeek и endOfWeek

Неделя — период, который зависит от локали и настроек начала недели.

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

startOfWeek

import { startOfWeek } from 'date-fns';

startOfWeek(new Date(2026, 0, 15));
// по умолчанию: воскресенье 11 января 2026

С настройкой понедельника как начала недели:

startOfWeek(new Date(2026, 0, 15), { weekStartsOn: 1 });
// 2026-01-12T00:00:00.000

endOfWeek

import { endOfWeek } from 'date-fns';

endOfWeek(new Date(2026, 0, 15), { weekStartsOn: 1 });
// 2026-01-18T23:59:59.999

Эти функции критичны для календарных интерфейсов и отчетов по неделям.


startOfHour, startOfMinute, startOfSecond

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

startOfHour

import { startOfHour } from 'date-fns';

startOfHour(new Date(2026, 0, 15, 14, 45, 30));
// 2026-01-15T14:00:00.000

endOfHour

import { endOfHour } from 'date-fns';

endOfHour(new Date(2026, 0, 15, 14, 45, 30));
// 2026-01-15T14:59:59.999

startOfMinute / endOfMinute

import { startOfMinute, endOfMinute } from 'date-fns';

startOfMinute(new Date(2026, 0, 15, 14, 45, 30));
// 2026-01-15T14:45:00.000

endOfMinute(new Date(2026, 0, 15, 14, 45, 30));
// 2026-01-15T14:45:59.999

startOfSecond / endOfSecond

import { startOfSecond, endOfSecond } from 'date-fns';

startOfSecond(new Date(2026, 0, 15, 14, 45, 30, 456));
// 2026-01-15T14:45:30.000

endOfSecond(new Date(2026, 0, 15, 14, 45, 30, 456));
// 2026-01-15T14:45:30.999

Поведение и важные особенности

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

Все функции возвращают новый объект:

const original = new Date();
const modified = startOfDay(original);

console.log(original === modified); // false

Локальное время

date-fns работает в локальной временной зоне среды выполнения. Это означает:

  • нет автоматической конвертации в UTC
  • результаты зависят от системной таймзоны

Пограничные значения

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

date <= endOfDay(target)

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

date >= startOfDay(target) && date < startOfDay(nextDay)

Комбинирование startOf и endOf

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

import { startOfMonth, endOfMonth } from 'date-fns';

const range = (date) => ({
  from: startOfMonth(date),
  to: endOfMonth(date),
});

Для фильтрации коллекций:

const filterByMonth = (items, date) => {
  const start = startOfMonth(date);
  const end = endOfMonth(date);

  return items.filter(item => item.createdAt >= start && item.createdAt <= end);
};

Типовые сценарии применения

Аналитика

  • группировка по дням, неделям, месяцам
  • агрегация метрик

UI-календарь

  • построение сетки месяца
  • подсветка текущих периодов

Бэкенд-фильтрация

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

Логирование

  • разбиение логов по часам или минутам

Поведение при переходе между месяцами и годами

Функции корректно обрабатывают:

  • конец февраля в високосный год
  • переход декабрь → январь
  • недели, пересекающие месяцы
endOfMonth(new Date(2024, 1, 10));
// 2024-02-29T23:59:59.999

Отличие startOf/endOf от ручных операций

Ручное обнуление часто приводит к ошибкам:

// небезопасно
date.setHours(0, 0, 0, 0);

В отличие от этого:

  • date-fns не мутирует исходную дату
  • код становится предсказуемым
  • учитываются крайние случаи календаря

Согласованность API

Все функции семейства имеют единый стиль:

  • startOfX(date, [options])
  • endOfX(date, [options])

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