Библиотека js-joda строится вокруг неизменяемых (immutable) объектов
времени: LocalDate, LocalTime,
LocalDateTime, Instant,
ZonedDateTime, Duration, Period.
Их прямое использование в прикладном коде быстро приводит к повторению
однотипных операций: форматирование дат, конвертация между типами,
проверка валидности входных значений, работа с часовыми поясами,
вычисление бизнес-границ (конец дня, начало недели, последний день
месяца).
Вспомогательные функции в таком контексте формируют слой абстракции между доменной логикой и низкоуровневым API библиотеки. Этот слой не изменяет поведение js-joda, но стандартизирует доступ к операциям времени и снижает связность кода.
Вспомогательные функции над js-joda обычно подчиняются нескольким устойчивым принципам:
1. Иммутабельность как основа Все функции принимают и возвращают новые экземпляры объектов времени без побочных эффектов.
2. Явное разделение ответственности Функция либо форматирует, либо парсит, либо преобразует — без смешивания задач.
3. Детерминированность Одинаковые входные данные всегда дают одинаковый результат, без скрытых зависимостей от локального состояния.
4. Изоляция внешних форматов Строки, числа Unix epoch, JSON-даты преобразуются на границе системы, а не внутри бизнес-логики.
В прикладных проектах часто требуется централизовать создание объектов времени. Это снижает вероятность ошибок при работе с часовыми поясами и форматами.
import { LocalDate, Instant, ZoneId, ZonedDateTime } from '@js-joda/core';
const DEFAULT_ZONE = ZoneId.of('UTC');
function nowInstant() {
return Instant.now();
}
function nowZoned(zone = DEFAULT_ZONE) {
return ZonedDateTime.now(zone);
}
function today(zone = DEFAULT_ZONE) {
return LocalDate.now(zone);
}
Такие функции фиксируют точку истины для получения текущего времени и позволяют при необходимости заменить источник времени (например, при тестировании).
Парсинг строковых дат — один из наиболее частых источников ошибок.
Вспомогательный слой обычно включает безопасные функции, возвращающие
null или Result-подобные структуры вместо
выбрасывания исключений.
import { LocalDate, DateTimeParseException } from '@js-joda/core';
function parseLocalDate(value) {
try {
return LocalDate.parse(value);
} catch (e) {
if (e instanceof DateTimeParseException) {
return null;
}
throw e;
}
}
Расширенный вариант с поддержкой нескольких форматов:
function parseLocalDateFlexible(value, parsers = []) {
for (const parser of parsers) {
try {
return parser(value);
} catch (_) {}
}
return null;
}
Пример использования:
const date = parseLocalDateFlexible('2026-01-25', [
LocalDate.parse,
(v) => LocalDate.parse(v, DateTimeFormatter.ISO_DATE)
]);
Форматирование дат в единый стиль особенно важно в API и UI-слое.
js-joda предоставляет DateTimeFormatter, но его
использование часто инкапсулируется.
import { DateTimeFormatter } from '@js-joda/core';
const ISO_DATE = DateTimeFormatter.ISO_DATE;
const ISO_DATETIME = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
function formatDate(date, formatter = ISO_DATE) {
return date.format(formatter);
}
Дополнительный слой может фиксировать корпоративные форматы:
const FORMATTERS = {
shortDate: DateTimeFormatter.ofPattern('dd.MM.yyyy'),
fullDateTime: DateTimeFormatter.ofPattern('dd.MM.yyyy HH:mm')
};
function format(value, type = 'shortDate') {
return value.format(FORMATTERS[type]);
}
В js-joda разные типы времени имеют строгие границы. Вспомогательные функции устраняют необходимость повторного написания однотипных преобразований.
import { Instant, ZoneId, LocalDateTime } from '@js-joda/core';
function instantToLocalDateTime(instant, zone = ZoneId.of('UTC')) {
return LocalDateTime.ofInstant(instant, zone);
}
function localDateTimeToInstant(localDateTime, zone = ZoneId.of('UTC')) {
return localDateTime.atZone(zone).toInstant();
}
Подобные функции фиксируют правила интерпретации времени, что особенно критично при работе с многозонными системами.
Бизнес-логика часто требует вычисления начала и конца периода: дня, недели, месяца.
import { LocalDate, LocalTime } from '@js-joda/core';
function startOfDay(date) {
return date.atStartOfDay();
}
function endOfDay(date) {
return date.atTime(LocalTime.MAX);
}
Расширение для недели:
import { DayOfWeek } from '@js-joda/core';
function startOfWeek(date, startDay = DayOfWeek.MONDAY) {
const diff = date.dayOfWeek().value() - startDay.value();
return date.minusDays((diff + 7) % 7);
}
function endOfWeek(date, startDay = DayOfWeek.MONDAY) {
return startOfWeek(date, startDay).plusDays(6);
}
Вспомогательные функции часто выходят за рамки простых преобразований и начинают отражать бизнес-правила.
Пример: расчёт возраста по дате рождения.
import { Period, LocalDate } from '@js-joda/core';
function calculateAge(birthDate, currentDate = LocalDate.now()) {
return Period.between(birthDate, currentDate).years();
}
Пример: проверка принадлежности к диапазону дат.
function isBetween(date, start, end) {
return !date.isBefore(start) && !date.isAfter(end);
}
Такие функции формируют доменный словарь времени, где операции читаются как бизнес-утверждения.
Вспомогательные функции над js-joda легко композиционно комбинируются.
function pipe(...fns) {
return (value) => fns.reduce((acc, fn) => fn(acc), value);
}
const normalizeDate = pipe(
parseLocalDate,
(d) => d?.plusDays(1) ?? null
);
Композиция позволяет строить цепочки преобразований без мутации состояния.
Для повторяющихся операций удобно фиксировать часть параметров заранее.
function addDays(days) {
return (date) => date.plusDays(days);
}
const addBusinessWeek = addDays(5);
Функции подобного типа хорошо интегрируются в пайплайны обработки данных.
Отдельный слой вспомогательных функций может отвечать за проверку корректности временных данных.
function assertValidLocalDate(date) {
if (!date || typeof date.isBefore !== 'function') {
throw new Error('Invalid LocalDate');
}
return date;
}
Более мягкий вариант:
function isValidLocalDate(date) {
return date && typeof date.isBefore === 'function';
}
Работа с зонами требует строгой дисциплины. Вспомогательные функции помогают централизовать правила конвертации.
import { ZoneId, ZonedDateTime, Instant } from '@js-joda/core';
function toZone(instant, zoneId = 'UTC') {
return instant.atZone(ZoneId.of(zoneId));
}
function fromZone(zonedDateTime) {
return zonedDateTime.toInstant();
}
Централизация этих операций снижает риск расхождений в интерпретации времени между слоями приложения.
Во многих API требуется работа с epoch-значениями.
function toEpochMillis(instant) {
return instant.toEpochMilli();
}
function fromEpochMillis(ms) {
return Instant.ofEpochMilli(ms);
}
Такие функции образуют границу между js-joda и внешними системами хранения и передачи данных.
Работа с временными интервалами часто требует собственных структур.
function createDateRange(start, end) {
return { start, end };
}
function contains(range, date) {
return isBetween(date, range.start, range.end);
}
Подобные конструкции позволяют формализовать диапазоны без введения тяжёлых классов.
Вспомогательный слой проектируется таким образом, чтобы каждая функция была изолирована и легко проверяема.
Пример тестируемого поведения:
const d = LocalDate.of(2026, 1, 1);
const result = addDays(10)(d);
result.toString(); // 2026-01-11
Отсутствие скрытых зависимостей делает функции детерминированными и предсказуемыми.
В крупных проектах вспомогательные функции обычно разделяются по смыслу:
date-create.js — создание дат и текущего времениdate-parse.js — парсинг строкdate-format.js — форматированиеdate-convert.js — преобразования типовdate-range.js — работа с диапазонамиdate-domain.js — бизнес-логикаТакая структура снижает когнитивную нагрузку и облегчает сопровождение кода.
Чем сложнее доменные правила, тем более оправдано их выносить во вспомогательные функции.
Пример: определение рабочей даты (исключая выходные).
function isWeekend(date) {
const day = date.dayOfWeek().value();
return day === 6 || day === 7;
}
function nextBusinessDay(date) {
let next = date.plusDays(1);
while (isWeekend(next)) {
next = next.plusDays(1);
}
return next;
}
Такая логика становится переиспользуемой и единообразной по всему приложению.