Skip to main content
Glama
date-parser.tsβ€’7.46 kB
/** * Date parsing utilities for natural language date expressions * Supports relative dates like "last week", "this month", etc. */ /** * Supported relative date formats */ export enum RelativeDateFormat { TODAY = 'today', YESTERDAY = 'yesterday', THIS_WEEK = 'this week', LAST_WEEK = 'last week', THIS_MONTH = 'this month', LAST_MONTH = 'last month', THIS_YEAR = 'this year', LAST_YEAR = 'last year', LAST_N_DAYS = 'last {n} days', LAST_N_WEEKS = 'last {n} weeks', LAST_N_MONTHS = 'last {n} months', } /** * Date range object for search operations */ export interface DateRange { start: string; // ISO date string end: string; // ISO date string } /** * Parse a relative date expression into a date range * @param expression Natural language date expression * @returns DateRange object with start and end dates * @throws Error if expression cannot be parsed */ export function parseRelativeDate(expression: string): DateRange { const normalized = expression.toLowerCase().trim(); const now = new Date(); // Helper to format date as ISO string (YYYY-MM-DD) const toISODate = (date: Date): string => { return date.toISOString().split('T')[0]; }; // Helper to get start of day const startOfDay = (date: Date): Date => { const d = new Date(date); d.setHours(0, 0, 0, 0); return d; }; // Helper to get start of week (Monday) const startOfWeek = (date: Date): Date => { const d = new Date(date); const day = d.getDay(); const diff = d.getDate() - day + (day === 0 ? -6 : 1); d.setDate(diff); return startOfDay(d); }; // Helper to get end of week (Sunday) const endOfWeek = (date: Date): Date => { const d = new Date(date); const day = d.getDay(); const diff = d.getDate() - day + (day === 0 ? 0 : 7); d.setDate(diff); return startOfDay(d); }; // Helper to get start of month const startOfMonth = (date: Date): Date => { const d = new Date(date); d.setDate(1); return startOfDay(d); }; // Helper to get end of month (last day of month) const endOfMonth = (date: Date): Date => { const d = new Date(date); d.setMonth(d.getMonth() + 1, 0); return startOfDay(d); }; // Helper to get start of year const startOfYear = (date: Date): Date => { const d = new Date(date); d.setMonth(0, 1); return startOfDay(d); }; // Helper to get end of year const endOfYear = (date: Date): Date => { const d = new Date(date); d.setMonth(11, 31); return startOfDay(d); }; // Parse specific relative dates switch (normalized) { case 'today': { const today = startOfDay(now); return { start: toISODate(today), end: toISODate(today), }; } case 'yesterday': { const yesterday = new Date(now); yesterday.setDate(yesterday.getDate() - 1); const startYesterday = startOfDay(yesterday); return { start: toISODate(startYesterday), end: toISODate(startYesterday), }; } case 'this week': { return { start: toISODate(startOfWeek(now)), end: toISODate(endOfWeek(now)), }; } case 'last week': { const lastWeek = new Date(now); lastWeek.setDate(lastWeek.getDate() - 7); return { start: toISODate(startOfWeek(lastWeek)), end: toISODate(endOfWeek(lastWeek)), }; } case 'this month': { return { start: toISODate(startOfMonth(now)), end: toISODate(endOfMonth(now)), }; } case 'last month': { const lastMonth = new Date(now); lastMonth.setMonth(lastMonth.getMonth() - 1); return { start: toISODate(startOfMonth(lastMonth)), end: toISODate(endOfMonth(lastMonth)), }; } case 'this year': { return { start: toISODate(startOfYear(now)), end: toISODate(endOfYear(now)), }; } case 'last year': { const lastYear = new Date(now); lastYear.setFullYear(lastYear.getFullYear() - 1); return { start: toISODate(startOfYear(lastYear)), end: toISODate(endOfYear(lastYear)), }; } } // Parse "last N days/weeks/months" patterns - handle spaces flexibly const lastNDaysMatch = normalized.match(/^last\s+(\d+)\s+days?$/); if (lastNDaysMatch) { const days = parseInt(lastNDaysMatch[1], 10); const startDate = new Date(now); startDate.setDate(startDate.getDate() - days); return { start: toISODate(startOfDay(startDate)), end: toISODate(startOfDay(now)), }; } const lastNWeeksMatch = normalized.match(/^last\s+(\d+)\s+weeks?$/); if (lastNWeeksMatch) { const weeks = parseInt(lastNWeeksMatch[1], 10); const startDate = new Date(now); startDate.setDate(startDate.getDate() - weeks * 7); return { start: toISODate(startOfDay(startDate)), end: toISODate(startOfDay(now)), }; } const lastNMonthsMatch = normalized.match(/^last\s+(\d+)\s+months?$/); if (lastNMonthsMatch) { const months = parseInt(lastNMonthsMatch[1], 10); const startDate = new Date(now); startDate.setMonth(startDate.getMonth() - months); return { start: toISODate(startOfDay(startDate)), end: toISODate(startOfDay(now)), }; } // If no pattern matches, throw an error throw new Error( `Unable to parse relative date expression: "${expression}". ` + `Supported formats: today, yesterday, this week, last week, this month, ` + `last month, this year, last year, last N days/weeks/months` ); } /** * Check if a string is a relative date expression * @param expression String to check * @returns true if the string is a recognized relative date expression */ export function isRelativeDate(expression: string): boolean { try { parseRelativeDate(expression); return true; } catch { return false; } } /** * Convert a date string or relative expression to ISO date format * @param dateInput Date string or relative expression * @returns ISO date string or null if invalid */ export function normalizeDate(dateInput: string): string | null { // Check if it's already an ISO date (YYYY-MM-DD) const isoDateRegex = /^\d{4}-\d{2}-\d{2}$/; if (isoDateRegex.test(dateInput)) { return dateInput; } // Check if it's a relative date if (isRelativeDate(dateInput)) { const range = parseRelativeDate(dateInput); // For single date context, return the start date return range.start; } // Try to parse as a regular date const date = new Date(dateInput); if (!isNaN(date.getTime())) { return date.toISOString().split('T')[0]; } return null; } /** * Get a human-readable description of a date range * @param range DateRange object * @returns Human-readable description */ export function describeDateRange(range: DateRange): string { // Parse dates as local dates to avoid timezone issues // Adding 'T00:00:00' ensures the date is interpreted in local time const start = new Date(range.start + 'T00:00:00'); const end = new Date(range.end + 'T00:00:00'); const formatDate = (date: Date): string => { return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }); }; // Check if it's a single day if (range.start === range.end) { return formatDate(start); } return `${formatDate(start)} to ${formatDate(end)}`; }

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/kesslerio/attio-mcp-server'

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