Временная модель js-joda основана на неизменяемых типах и календарной
арифметике, где базовыми единицами выступают даты, время и временные
интервалы без привязки к мутабельному состоянию. Рабочие дни не являются
встроенной сущностью ISO-8601, поэтому их логика строится поверх
LocalDate, DayOfWeek и пользовательских правил
календаря.
Рабочий день определяется как дата, удовлетворяющая условиям:
Основной инструмент работы — LocalDate, позволяющий
оперировать датами без времени и временных зон.
Класс DayOfWeek задаёт стандартизированное представление
дней недели от 1 (Monday) до 7 (Sunday).
import { LocalDate, DayOfWeek } from '@js-joda/core';
const date = LocalDate.parse('2026-01-24');
const dayOfWeek = date.dayOfWeek();
const isWeekend =
dayOfWeek.equals(DayOfWeek.SATURDAY) ||
dayOfWeek.equals(DayOfWeek.SUNDAY);
На этом уровне формируется фундамент логики рабочих дней: любая операция сводится к проверке принадлежности даты к допустимому множеству.
Базовая абстракция — функция-предикат, определяющая валидность даты:
const isWorkingDay = (date) => {
const dow = date.dayOfWeek();
const isWeekend =
dow.equals(DayOfWeek.SATURDAY) ||
dow.equals(DayOfWeek.SUNDAY);
return !isWeekend;
};
Эта функция расширяется при подключении праздничных календарей.
Рабочие дни в реальных системах зависят от набора фиксированных и плавающих праздников.
const holidays = new Set([
'2026-01-01',
'2026-03-08',
'2026-05-01'
]);
const isHoliday = (date) =>
holidays.has(date.toString());
const isWorkingDay = (date) =>
!isHoliday(date) &&
!date.dayOfWeek().equals(DayOfWeek.SATURDAY) &&
!date.dayOfWeek().equals(DayOfWeek.SUNDAY);
Такая модель остаётся полностью детерминированной и не зависит от внешнего состояния.
Одной из базовых операций является поиск ближайшего рабочего дня вперёд.
const nextWorkingDay = (date) => {
let current = date.plusDays(1);
while (!isWorkingDay(current)) {
current = current.plusDays(1);
}
return current;
};
Метод plusDays из js-joda обеспечивает неизменяемость:
каждый шаг создаёт новый экземпляр LocalDate.
Аналогичная логика применяется в обратном направлении:
const previousWorkingDay = (date) => {
let current = date.minusDays(1);
while (!isWorkingDay(current)) {
current = current.minusDays(1);
}
return current;
};
Такая операция используется в расчётах дедлайнов и ретроспективных периодов.
Обычное добавление дней через plusDays не учитывает
календарные ограничения. Поэтому вводится специализированная
функция.
const addWorkingDays = (date, days) => {
let current = date;
let remaining = days;
while (remaining > 0) {
current = current.plusDays(1);
if (isWorkingDay(current)) {
remaining -= 1;
}
}
return current;
};
Алгоритм последовательно проходит календарь, игнорируя нерабочие даты.
const subtractWorkingDays = (date, days) => {
let current = date;
let remaining = days;
while (remaining > 0) {
current = current.minusDays(1);
if (isWorkingDay(current)) {
remaining -= 1;
}
}
return current;
};
Операция симметрична добавлению, но двигается в обратном направлении по временной шкале.
Частая задача — вычисление количества рабочих дней между двумя датами включительно или исключительно.
const countWorkingDays = (start, end) => {
let count = 0;
let current = start;
while (current.isBefore(end)) {
if (isWorkingDay(current)) {
count += 1;
}
current = current.plusDays(1);
}
if (isWorkingDay(end)) {
count += 1;
}
return count;
};
Такая модель применяется в SLA-расчётах и планировании загрузки.
Архитектура js-joda поддерживает концепцию
TemporalAdjuster, позволяющую инкапсулировать правила
преобразования дат.
const nextWorkingDayAdjuster = (date) => {
let adjusted = date.plusDays(1);
while (!isWorkingDay(adjusted)) {
adjusted = adjusted.plusDays(1);
}
return adjusted;
};
Применение:
const result = nextWorkingDayAdjuster(LocalDate.parse('2026-01-23'));
В экосистеме существует расширение, реализующее бизнес-календарь на
уровне библиотеки: js-joda-business-days. Оно предоставляет
готовые инструменты для работы с рабочими днями и календарными
правилами.
Типовая модель включает:
Пример концептуального использования:
import { LocalDate } from '@js-joda/core';
import { BusinessDays } from 'js-joda-business-days';
const calendar = BusinessDays.of({
holidays: ['2026-01-01', '2026-03-08'],
weekend: [6, 7]
});
const date = LocalDate.parse('2026-01-20');
const result = calendar.plusWorkingDays(date, 5);
Абстракция библиотеки заменяет ручные циклы декларативными операциями.
Диапазоны используются для анализа временных интервалов.
const range = {
start: LocalDate.parse('2026-01-01'),
end: LocalDate.parse('2026-01-31')
};
const workingDays = [];
let current = range.start;
while (!current.isAfter(range.end)) {
if (isWorkingDay(current)) {
workingDays.push(current);
}
current = current.plusDays(1);
}
Такая структура применяется при формировании графиков работ и финансовых расчётов.
При частых вычислениях рабочих дней критически важно избегать повторной проверки одинаковых дат. Распространённый подход — кэширование результата предиката.
const cache = new Map();
const isWorkingDayCached = (date) => {
const key = date.toString();
if (cache.has(key)) {
return cache.get(key);
}
const value = isWorkingDay(date);
cache.set(key, value);
return value;
};
Это снижает стоимость вычислений в больших диапазонах.
Логика рабочих дней используется в системах:
Во всех случаях js-joda обеспечивает стабильную основу временной
модели, а бизнес-правила реализуются поверх LocalDate и
пользовательских функций.