Работа с праздниками

Moment.js изначально не предоставляет встроенной поддержки праздников, однако библиотека обладает гибкими инструментами для работы с датами, что позволяет реализовать обработку праздников самостоятельно или интегрировать внешние источники данных о праздничных днях. В данной главе рассмотрены подходы к определению, проверке и управлению праздниками с использованием возможностей Moment.js.


Определение праздников

Праздники можно хранить в виде массива объектов или строк, где каждая запись соответствует конкретной дате. Для удобства используется формат ISO 8601 (YYYY-MM-DD), который Moment.js обрабатывает без дополнительных преобразований:

const holidays = [
    "2026-01-01", // Новый год
    "2026-05-01", // Праздник Весны и Труда
    "2026-12-25"  // Рождество
];

Для динамических праздников, например, Пасха, создаются функции вычисления даты в зависимости от года:

function calculateEaster(year) {
    const f = Math.floor,
          G = year % 19,
          C = f(year / 100),
          H = (C - f(C / 4) - f((8 * C + 13) / 25) + 19 * G + 15) % 30,
          I = H - f(H / 28) * (1 - f(H / 28) * f(29 / (H + 1)) * f((21 - G) / 11)),
          J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7,
          L = I - J,
          month = 3 + f((L + 40) / 44),
          day = L + 28 - 31 * f(month / 4);
    return moment(`${year}-${month}-${day}`, "YYYY-M-D");
}

const easter2026 = calculateEaster(2026);

Проверка даты на праздник

Для проверки, является ли определённая дата праздничной, создаётся функция, сравнивающая даты с массивом праздников:

function isHoliday(date, holidaysList) {
    const formattedDate = moment(date).format("YYYY-MM-DD");
    return holidaysList.includes(formattedDate);
}

const someDate = moment("2026-05-01");
console.log(isHoliday(someDate, holidays)); // true

В случае динамических праздников можно расширить функцию:

const dynamicHolidays = [
    calculateEaster(2026).format("YYYY-MM-DD")
];

function isHolidayExtended(date) {
    const formatted = moment(date).format("YYYY-MM-DD");
    return holidays.includes(formatted) || dynamicHolidays.includes(formatted);
}

Получение списка праздников за период

Для анализа календаря часто требуется получить все праздники за определённый промежуток времени. Используется метод moment.range (из библиотеки moment-range), который позволяет итерировать даты в заданном диапазоне:

const { extendMoment } = require('moment-range');
const Moment = require('moment');
const moment = extendMoment(Moment);

const startDate = moment("2026-01-01");
const endDate = moment("2026-12-31");
const range = moment.range(startDate, endDate);

const holidaysInYear = Array.from(range.by('day')).filter(d => isHolidayExtended(d));
console.log(holidaysInYear.map(d => d.format("YYYY-MM-DD")));

Определение рабочих и выходных дней

Для расчётов с праздниками важно учитывать рабочие и выходные дни. В Moment.js нет встроенной логики для национальных календарей, но можно реализовать простую проверку:

function isWeekend(date) {
    const day = moment(date).day(); // 0 — Воскресенье, 6 — Суббота
    return day === 0 || day === 6;
}

function isWorkday(date) {
    return !isWeekend(date) && !isHolidayExtended(date);
}

Использование функции позволяет фильтровать массив дат для составления рабочих графиков:

const workdaysInMay = Array.from(moment.range("2026-05-01", "2026-05-31").by('day'))
                             .filter(isWorkday)
                             .map(d => d.format("YYYY-MM-DD"));

Добавление пользовательских правил праздников

Иногда требуется вводить региональные праздники или переносы праздничных дней на ближайший рабочий день. Можно реализовать функцию переноса:

function shiftHolidayIfWeekend(date) {
    let m = moment(date);
    if (m.day() === 6) m.add(2, 'days'); // перенос с субботы на понедельник
    if (m.day() === 0) m.add(1, 'days'); // перенос с воскресенья на понедельник
    return m;
}

const shiftedHolidays = holidays.map(shiftHolidayIfWeekend);

Таким образом обеспечивается корректное отображение праздников в рабочем календаре.


Интеграция с внешними календарями

Для более сложных задач можно использовать API праздничных календарей, которые возвращают даты в формате JSON. После получения данных Moment.js позволяет преобразовать строки в объекты moment и выполнять все перечисленные проверки и фильтрации:

fetch("https://example.com/api/holidays?year=2026")
    .then(res => res.json())
    .then(data => {
        const apiHolidays = data.map(d => moment(d.date));
        const combinedHolidays = [...holidays, ...apiHolidays];
        console.log(isHoliday("2026-12-25", combinedHolidays));
    });

Форматирование праздничных дат

Moment.js предоставляет удобные методы для форматирования дат:

const holiday = moment("2026-01-01");
console.log(holiday.format("DD MMMM YYYY"));  // 01 января 2026
console.log(holiday.format("dddd, MMMM Do YYYY")); // четверг, января 1-го 2026

Это позволяет создавать визуально понятные календарные отчёты и интегрировать даты в интерфейс приложений.


Вывод

Комбинация возможностей Moment.js по работе с датами, массивами и диапазонами позволяет создавать гибкие системы для управления праздниками, определения рабочих и выходных дней, реализации переносов и интеграции внешних календарей. Использование стандартных методов форматирования обеспечивает удобное представление данных для дальнейшей обработки или отображения.