next_occurrence
Calculate the next occurrence of a recurring event based on specified patterns like daily, weekly, monthly, or yearly schedules.
Instructions
Find next occurrence of a recurring event
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| pattern | Yes | Recurrence pattern | |
| start_from | No | Start searching from | |
| day_of_week | No | For weekly (0-6, 0=Sunday) | |
| day_of_month | No | For monthly (1-31) | |
| time | No | Time in HH:mm format | |
| timezone | No | Timezone for calculation (default: system timezone) |
Implementation Reference
- src/tools/nextOccurrence.ts:158-189 (handler)The core handler function implementing the next_occurrence tool logic, including parameter validation, timezone resolution, caching, and delegation to calculation logic.export function nextOccurrence(params: NextOccurrenceParams): NextOccurrenceResult { debug.recurrence('nextOccurrence called with params: %O', params); // Validate string length first if (params.start_from && typeof params.start_from === 'string') { validateDateString(params.start_from, 'start_from'); } const config = getConfig(); const fallbackTimezone = config.defaultTimezone; const timezone = resolveTimezone(params.timezone, fallbackTimezone); // Validate timezone if provided if (params.timezone) { debug.validation('Validating timezone: %s', timezone); if (!validateTimezone(timezone)) { debug.error('Invalid timezone: %s', timezone); throw new TimezoneError(`Invalid timezone: ${timezone}`, timezone); } } const cacheKey = getCacheKey(params, fallbackTimezone, timezone); // Use withCache wrapper instead of manual cache management return withCache(cacheKey, CacheTTL.CALCULATIONS, () => { try { const result = calculateNextOccurrence(params, timezone); debug.recurrence('nextOccurrence returning: %O', result); return result; } catch (error) { handleCalculationError(error); } }); }
- src/types/index.ts:113-126 (schema)TypeScript type definitions for input parameters (NextOccurrenceParams) and output result (NextOccurrenceResult) used by the handler.export interface NextOccurrenceParams { pattern: RecurrencePattern; start_from?: string; day_of_week?: number; day_of_month?: number; time?: string; timezone?: string; } export interface NextOccurrenceResult { next: string; unix: number; days_until: number; }
- src/index.ts:157-179 (registration)MCP tool registration in TOOL_DEFINITIONS array, including the tool name, description, and input schema.{ name: 'next_occurrence', description: 'Find next occurrence of a recurring event', inputSchema: { type: 'object' as const, properties: { pattern: { type: 'string' as const, enum: ['daily', 'weekly', 'monthly', 'yearly'], description: 'Recurrence pattern', }, start_from: { type: 'string' as const, description: 'Start searching from' }, day_of_week: { type: 'number' as const, description: 'For weekly (0-6, 0=Sunday)' }, day_of_month: { type: 'number' as const, description: 'For monthly (1-31)' }, time: { type: 'string' as const, description: 'Time in HH:mm format' }, timezone: { type: 'string' as const, description: 'Timezone for calculation (default: system timezone)', }, }, required: ['pattern'], }, },
- src/index.ts:268-274 (registration)Mapping of the 'next_occurrence' tool name to the nextOccurrence handler function in TOOL_FUNCTIONS record.next_occurrence: (params: unknown) => nextOccurrence(params as Parameters<typeof nextOccurrence>[0]), format_time: (params: unknown) => formatTime(params as Parameters<typeof formatTime>[0]), calculate_business_hours: (params: unknown) => calculateBusinessHours(params as Parameters<typeof calculateBusinessHours>[0]), days_until: (params: unknown) => daysUntil(params as Parameters<typeof daysUntil>[0]), };
- src/tools/nextOccurrence.ts:99-136 (helper)Internal helper function that performs the core date calculation using the RecurrenceFactory.function calculateNextOccurrence( params: NextOccurrenceParams, timezone: string ): NextOccurrenceResult { debug.recurrence('calculateNextOccurrence called with params: %O', params); // Parse start date let startFrom: Date; if (params.start_from) { debug.parse('Parsing start_from: %s', params.start_from); try { startFrom = parseTimeInput(params.start_from, timezone).date; debug.parse('Parsed start_from date: %s', startFrom.toISOString()); } catch { debug.error('Invalid start_from date: %s', params.start_from); throw new DateParsingError('Invalid start_from date', { start_from: params.start_from }); } } else { startFrom = new Date(); } // Map parameters to new format const recurrenceParams = mapToRecurrenceParams(params); recurrenceParams.timezone = timezone; // Calculate next occurrence using factory debug.recurrence('Calculating next occurrence with factory'); const nextDate = factory.calculate(startFrom, recurrenceParams); debug.recurrence('Next occurrence date: %s', nextDate.toISOString()); // Format result const result: NextOccurrenceResult = { next: nextDate.toISOString(), unix: Math.floor(nextDate.getTime() / 1000), days_until: calculateDaysUntil(nextDate, timezone), }; return result; }