Рабочие дни в JavaScript-проектах обычно трактуются как календарные дни без выходных (суббота и воскресенье) и, в более сложных сценариях, без государственных праздников. В библиотеке Date-fns базовая поддержка рабочих дней реализована через набор функций, ориентированных на вычисления дат с исключением выходных.
Ключевая идея заключается в том, что операции добавления, вычитания и сравнения дат могут учитывать только те дни, которые удовлетворяют условию «не выходной».
Основой для вычисления рабочих дней служит функция определения выходных:
import { isWeekend } from 'date-fns';
isWeekend(new Date(2026, 0, 10));
Функция isWeekend(date) возвращает true,
если дата попадает на субботу или воскресенье.
Логика внутри основана на значении дня недели:
Таким образом, любой алгоритм определения рабочих дней обычно строится как отрицание этой функции.
const isBusinessDay = (date) => !isWeekend(date);
Такой подход используется как базовый слой для более сложных операций.
Для корректного смещения даты с учётом только рабочих дней используется функция:
import { addBusinessDays } from 'date-fns';
const result = addBusinessDays(new Date(2026, 0, 1), 5);
addBusinessDays(date, amount) последовательно
увеличивает дату, пропуская выходные дни.
Особенности поведения:
amount не поддерживается (для
этого используется subBusinessDays)Пример:
import { addBusinessDays } from 'date-fns';
const start = new Date(2026, 0, 9); // пятница
const end = addBusinessDays(start, 1); // следующий рабочий день
console.log(end);
Если следующий день — суббота, функция перескочит сразу на понедельник.
Обратная операция выполняется через subBusinessDays:
import { subBusinessDays } from 'date-fns';
const result = subBusinessDays(new Date(2026, 0, 12), 3);
Функция движется назад по календарю, игнорируя выходные.
Алгоритмически она зеркальна addBusinessDays, но
направление смещения противоположное.
Для вычисления количества рабочих дней между датами используется:
import { differenceInBusinessDays } from 'date-fns';
const diff = differenceInBusinessDays(
new Date(2026, 0, 20),
new Date(2026, 0, 1)
);
Функция возвращает число полных рабочих дней между датами.
Особенности:
Пример интерпретации:
differenceInBusinessDays(new Date(2026, 0, 5), new Date(2026, 0, 1));
Если в интервале есть выходные, они полностью исключаются из подсчёта.
Во многих прикладных задачах одних выходных недостаточно. Часто требуется учитывать праздники или корпоративные нерабочие дни.
Базовая расширяемая модель:
import { isWeekend, isSameDay } from 'date-fns';
const holidays = [
new Date(2026, 0, 1),
new Date(2026, 4, 9)
];
const isBusinessDay = (date) => {
const weekend = isWeekend(date);
const holiday = holidays.some(h => isSameDay(h, date));
return !weekend && !holiday;
};
Такой подход позволяет внедрить кастомную бизнес-логику без изменения базовых функций Date-fns.
Для анализа диапазона дат часто используется комбинация
eachDayOfInterval и фильтрации:
import { eachDayOfInterval, isWeekend } from 'date-fns';
const range = eachDayOfInterval({
start: new Date(2026, 0, 1),
end: new Date(2026, 0, 15)
});
const businessDays = range.filter(date => !isWeekend(date));
Этот метод полезен, когда требуется:
При добавлении праздников фильтрация расширяется:
const businessDays = range.filter(date =>
!isWeekend(date) &&
!holidays.some(h => isSameDay(h, date))
);
Расчёт рабочих дней можно рассматривать как функцию:
B(d_1, d_2) = _{d=d_1}^{d_2} f(d)
где:
Такая модель позволяет формализовать любую бизнес-логику, расширяя условие функции ( f(d) ).
Date-fns оперирует объектами Date, которые зависят от
локального времени окружения выполнения.
При работе с рабочими днями возникают типичные нюансы:
23:59 может относиться к другому дню в
UTCisSameDay зависит от локальной
зоныДля минимизации ошибок применяется нормализация:
const normalized = new Date(date.getFullYear(), date.getMonth(), date.getDate());
Такой подход фиксирует дату на уровне календарного дня.
В прикладных системах часто требуется комбинация нескольких функций:
Пример комбинированной логики:
import { addDays, isWeekend } from 'date-fns';
const addCustomBusinessDays = (date, amount, holidays = []) => {
let result = new Date(date);
let added = 0;
while (added < amount) {
result = addDays(result, 1);
const isHoliday = holidays.some(h => isSameDay(h, result));
if (!isWeekend(result) && !isHoliday) {
added++;
}
}
return result;
};
Такой алгоритм используется, когда стандартные функции Date-fns недостаточны.
При обработке больших интервалов дат важно учитывать сложность операций:
eachDayOfInterval создаёт массив всех дней
диапазонаОптимизация достигается через:
isSameDayconst holidaySet = new Set(holidays.map(d => d.getTime()));
const isHoliday = (date) => holidaySet.has(date.getTime());
Внутренний принцип большинства функций Date-fns для бизнес-дней основан на последовательной итерации:
Эта модель гарантирует корректность независимо от календарных аномалий, таких как:
В корпоративных системах часто требуется поддержка:
Абстракция функции проверки дня позволяет расширять поведение:
const createBusinessDayChecker = (holidays, extraRules) => {
const holidaySet = new Set(holidays.map(d => d.getTime()));
return (date) => {
const isHoliday = holidaySet.has(date.getTime());
const isWeekendDay = isWeekend(date);
return !isHoliday && !isWeekendDay && extraRules(date);
};
};
Такой подход делает вычисление рабочих дней полностью конфигурируемым.