Библиотека js-joda предоставляет строгую, иммутабельную модель работы
с датой и временем, где ключевую роль играют типы
LocalDate, LocalDateTime,
ZonedDateTime, Duration и Period.
Однако в реальных бизнес-сценариях этого недостаточно: поверх базовых
типов формируется слой утилит, реализующих правила предметной области —
расчёт сроков, дедлайнов, рабочих дней, окон доступности и SLA.
Бизнес-логика времени почти всегда опирается на три категории операций:
В js-joda сравнение дат осуществляется через методы
isBefore, isAfter, isEqual. На их
основе строятся предикаты бизнес-логики.
import { LocalDate } from '@js-joda/core';
const today = LocalDate.now();
const deadline = LocalDate.parse('2026-06-01');
const isOverdue = today.isAfter(deadline);
const isNotStarted = today.isBefore(deadline);
В бизнес-слое такие проверки редко остаются в сыром виде. Обычно они оборачиваются в функции:
const isExpired = (currentDate, expirationDate) =>
currentDate.isAfter(expirationDate);
const isActive = (currentDate, start, end) =>
(currentDate.isEqual(start) || currentDate.isAfter(start)) &&
(currentDate.isBefore(end) || currentDate.isEqual(end));
Подобные предикаты становятся строительными блоками правил доступа, подписок, бронирований и ограничений.
Для бизнес-логики критичны Period и
Duration. Они разделяют два подхода:
Period — календарные единицы (дни, месяцы, годы)Duration — абсолютное время (секунды,
миллисекунды)import { LocalDate, Period } from '@js-joda/core';
const start = LocalDate.parse('2026-01-01');
const end = start.plus(Period.ofMonths(3));
Типовой бизнес-кейс — срок действия подписки:
const addSubscriptionPeriod = (startDate, months) =>
startDate.plus(Period.ofMonths(months));
В отличие от арифметики на числах, Period учитывает
календарную структуру, включая разную длину месяцев и високосные
годы.
Многие системы требуют приведения дат к определённым границам: начало дня, конец месяца, первый рабочий день периода.
import { LocalDate, LocalTime, LocalDateTime } from '@js-joda/core';
const toStartOfDay = (date) =>
LocalDateTime.of(date, LocalTime.MIDNIGHT);
const toEndOfDay = (date) =>
LocalDateTime.of(date, LocalTime.of(23, 59, 59));
Для финансовых систем часто используется нормализация к концу месяца:
const toEndOfMonth = (date) =>
date.withDayOfMonth(date.lengthOfMonth());
Такие преобразования формируют единообразие хранения и сравнения временных значений.
В бизнес-системах часто присутствуют SLA-правила: “ответить в течение N дней”, “обработать до конца рабочего дня”.
import { LocalDate, Period } from '@js-joda/core';
const addBusinessDays = (date, days) => {
let result = date;
let added = 0;
while (added < days) {
result = result.plusDays(1);
if (result.dayOfWeek().value() < 6) {
added++;
}
}
return result;
};
Такая функция формирует базу для расчёта сроков с учётом выходных.
SLA-расчёт:
const calculateSlaDeadline = (createdAt, slaDays) =>
addBusinessDays(createdAt, slaDays);
Бизнес-логика часто оперирует интервалами: окна доставки, доступность сервисов, время приёма заявок.
const isWithinWindow = (time, start, end) =>
(time.equals(start) || time.isAfter(start)) &&
time.isBefore(end);
Для более сложных сценариев используются комбинированные проверки:
const isServiceAvailable = (dateTime) => {
const hour = dateTime.hour();
const isWorkingHours = hour >= 9 && hour < 18;
const isWeekday = dateTime.dayOfWeek().value() <= 5;
return isWorkingHours && isWeekday;
};
Бизнес-логика часто требует проверки корректности временных диапазонов:
const validateRange = (start, end) => {
if (end.isBefore(start) || end.isEqual(start)) {
throw new Error('Некорректный временной диапазон');
}
};
Расширенный вариант включает контроль пересечений:
const hasOverlap = (aStart, aEnd, bStart, bEnd) =>
aStart.isBefore(bEnd) && bStart.isBefore(aEnd);
Эти проверки используются в бронировании ресурсов, планировании задач и расписаниях.
ChronoUnit позволяет измерять разницу между датами в
различных единицах:
import { ChronoUnit, LocalDate } from '@js-joda/core';
const start = LocalDate.parse('2026-01-01');
const end = LocalDate.parse('2026-02-15');
const daysBetween = ChronoUnit.DAYS.between(start, end);
На основе этого строятся метрики:
const isLongRunning = (start, end) =>
ChronoUnit.DAYS.between(start, end) > 30;
В системах отчетности часто требуется округление времени до дня, недели или месяца.
const truncateToMonth = (date) =>
date.withDayOfMonth(1);
Для недельных отчётов:
const truncateToWeek = (date) => {
const dayOfWeek = date.dayOfWeek().value();
return date.minusDays(dayOfWeek - 1);
};
Такие функции формируют основу для аналитических систем.
При работе с глобальными системами критично учитывать
ZonedDateTime.
import { ZonedDateTime, ZoneId } from '@js-joda/core';
const zoned = ZonedDateTime.now(ZoneId.of('Europe/Amsterdam'));
Проверка локального рабочего времени:
const isOfficeOpen = (zonedDateTime) => {
const hour = zonedDateTime.hour();
return hour >= 9 && hour < 17;
};
Без учёта зон времени бизнес-логика быстро становится некорректной при международных сценариях.
На практике утилиты не используются изолированно. Они формируют цепочки правил:
const isOrderValid = (orderDate, deliveryDate) => {
validateRange(orderDate, deliveryDate);
const isNotTooFar =
ChronoUnit.DAYS.between(orderDate, deliveryDate) <= 30;
const isWeekdayOrder = orderDate.dayOfWeek().value() <= 5;
return isNotTooFar && isWeekdayOrder;
};
Здесь объединяются:
Бизнес-утилиты удобно строить как чистые функции:
const and = (a, b) => (x) => a(x) && b(x);
const isWeekday = (date) => date.dayOfWeek().value() <= 5;
const isNotHoliday = (date) => true;
const isWorkingDay = and(isWeekday, isNotHoliday);
Такой подход позволяет расширять правила без изменения существующей логики.
plusDays и
PeriodChronoUnitБиблиотека js-joda выступает фундаментом, но бизнес-ценность возникает только при формировании слоя утилит:
Именно этот слой превращает работу с датами из технической операции в часть предметной модели системы.