Skip to main content
Glama
pshempel

MCP Time Server Node

by pshempel
holidayAggregator.ts5.45 kB
/** * Holiday Aggregator Utility * * Consolidates holiday dates from multiple sources into a single Set. * Reduces ~60 lines of complex holiday aggregation logic to a single function call. * * Handles: * - Calendar holidays from country codes (with observed dates) * - Custom holiday date strings * - Legacy holiday parameter for backward compatibility * - Date range filtering * - Deduplication across sources */ import { DateParsingError } from '../adapters/mcp-sdk/errors'; import { getHolidaysForYear } from '../data/holidays'; import { parseHolidayDates } from './businessUtils'; import { debug } from './debug'; export interface HolidayAggregatorParams { /** Country calendar code (e.g., 'US', 'UK') */ calendar?: string; /** Custom holiday date strings */ custom?: string[]; /** Legacy holiday parameter for backward compatibility */ legacy?: string[]; /** Whether to use observed dates for calendar holidays */ includeObserved: boolean; /** Timezone for parsing dates */ timezone: string; /** Date range to fetch calendar holidays */ dateRange: { start: Date; end: Date; }; } /** * Aggregates holidays from multiple sources into a single Set * * @param params - Holiday aggregation parameters * @returns Set of holiday date strings (using toDateString() format) * @throws Error if calendar code is invalid or custom holidays can't be parsed * * @example * ```typescript * const holidays = aggregateHolidays({ * calendar: 'US', * custom: ['2025-03-15'], * legacy: ['2025-06-20'], * includeObserved: true, * timezone: 'America/New_York', * dateRange: { start: new Date('2025-01-01'), end: new Date('2025-12-31') } * }); * // Returns Set with all unique holiday dates * ``` */ export function aggregateHolidays(params: HolidayAggregatorParams): Set<string> { const { calendar, custom = [], legacy = [], includeObserved, timezone, dateRange } = params; const allHolidayDates = new Set<string>(); const { start, end } = dateRange; debug.business( 'Aggregating holidays from %d sources', [calendar, custom.length > 0, legacy.length > 0].filter(Boolean).length ); // 1. Add calendar holidays if specified if (calendar) { addCalendarHolidays(allHolidayDates, calendar, includeObserved, start, end); } // 2. Add custom holidays if (custom.length > 0) { let customDates: Date[]; try { customDates = parseHolidayDates(custom, timezone); } catch (error) { // Re-throw with more specific error message for custom holidays const errorObj = error as { error?: { message?: string; data?: { holiday?: string } } }; const originalMessage = errorObj.error?.message ?? String(error); const invalidHoliday = errorObj.error?.data?.holiday; debug.error( 'Invalid custom holiday date: %s, error: %s', invalidHoliday ?? 'unknown', originalMessage ); throw new DateParsingError(`Invalid custom holiday date: ${invalidHoliday ?? 'unknown'}`, { holiday: invalidHoliday, error: originalMessage, }); } let addedCount = 0; for (const date of customDates) { // Check if holiday is within date range if (date >= start && date <= end) { allHolidayDates.add(date.toDateString()); addedCount++; } } debug.business('Added %d custom holidays (from %d provided)', addedCount, custom.length); } // 3. Add legacy holidays (backward compatibility) if (legacy.length > 0) { const legacyDates = parseHolidayDates(legacy, timezone); let addedCount = 0; for (const date of legacyDates) { // Check if holiday is within date range if (date >= start && date <= end) { allHolidayDates.add(date.toDateString()); addedCount++; } } debug.business('Added %d legacy holidays (from %d provided)', addedCount, legacy.length); } debug.business('Total unique holidays aggregated: %d', allHolidayDates.size); return allHolidayDates; } /** * Helper function to add calendar holidays to the set * Extracted to reduce nesting and complexity */ function addCalendarHolidays( holidaySet: Set<string>, calendar: string, includeObserved: boolean, start: Date, end: Date ): void { const startYear = start.getFullYear(); const endYear = end.getFullYear(); const initialSize = holidaySet.size; for (let year = startYear; year <= endYear; year++) { const holidays = getHolidaysForYear(calendar, year); for (const holiday of holidays) { // Use observed date if available and include_observed is true const dateToUse = includeObserved && holiday.observedDate ? holiday.observedDate : holiday.date; // Add holiday date string - no need to filter by range here // as getBusinessDays will only count holidays that fall on actual days in the interval holidaySet.add(dateToUse.toDateString()); } } debug.business('Added %d calendar holidays from %s', holidaySet.size - initialSize, calendar); } /** * Validates a holiday calendar code * * @param calendar - Calendar code to validate * @returns true if valid, false otherwise */ export function isValidCalendarCode(calendar: string): boolean { try { // Try to get holidays for current year as a validation check getHolidaysForYear(calendar, new Date().getFullYear()); return true; } catch { return false; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pshempel/mcp-time-server-node'

If you have feedback or need assistance with the MCP directory API, please join our Discord server