UNPKG

@kermank/nldp

Version:

A modular date/time parser for converting natural language into dates and times

209 lines 7.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.absoluteDatesRule = void 0; const Logger_1 = require("../utils/Logger"); const luxon_1 = require("luxon"); function createDateComponent(date, span, originalText, preferences) { return { type: 'date', span, value: date, confidence: 1, metadata: { originalText, dateType: 'absolute' } }; } const MONTHS = { 'january': 1, 'jan': 1, 'february': 2, 'feb': 2, 'march': 3, 'mar': 3, 'april': 4, 'apr': 4, 'may': 5, 'june': 6, 'jun': 6, 'july': 7, 'jul': 7, 'august': 8, 'aug': 8, 'september': 9, 'sep': 9, 'october': 10, 'oct': 10, 'november': 11, 'nov': 11, 'december': 12, 'dec': 12 }; function parseMonthName(monthStr) { const month = MONTHS[monthStr.toLowerCase()]; return month || 0; } function isValidDate(year, month, day) { if (month < 1 || month > 12) return false; const lastDay = new Date(Date.UTC(year, month, 0)).getUTCDate(); return day >= 1 && day <= lastDay; } function createDateParser(format) { return (matches, preferences) => { const [_, ...parts] = matches; let year, month, day; switch (format) { case 'YMD': [year, month, day] = parts.map(p => parseInt(p)); break; case 'MDY': [month, day, year] = parts.map(p => parseInt(p)); break; case 'DMY': [day, month, year] = parts.map(p => parseInt(p)); break; default: return null; } // Handle 2-digit years if (year < 100) { year += year < 50 ? 2000 : 1900; } const date = luxon_1.DateTime.utc(year, month, day); if (!date.isValid) { return null; } return { type: 'single', start: date, confidence: 1, text: matches[0] }; }; } function createMonthNameParser(format) { return (matches, preferences) => { var _a; Logger_1.Logger.debug('Parsing month name date', { format, matches: matches.map(m => m), }); let year, month, day; const currentYear = ((_a = preferences.referenceDate) === null || _a === void 0 ? void 0 : _a.toUTC().year) || new Date().getUTCFullYear(); if (format === 'MonthFirst') { month = parseMonthName(matches[1]); day = parseInt(matches[2]); year = matches[3] ? parseInt(matches[3]) : currentYear; } else { day = parseInt(matches[1]); month = parseMonthName(matches[2]); year = matches[3] ? parseInt(matches[3]) : currentYear; } if (!isValidDate(year, month, day)) { Logger_1.Logger.debug('Invalid date components', { year, month, day }); return null; } const result = luxon_1.DateTime.utc(year, month, day); return { type: 'single', start: result, confidence: 1.0, text: matches[0] }; }; } const patterns = [ { regex: /^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{1,2}):(\d{2}))?(?:\s*([+-]\d{4})?)?$/, parse: (matches, preferences) => { const [fullMatch, year, month, day, hours, minutes, timezone] = matches; // Create base date in UTC let date = luxon_1.DateTime.utc(parseInt(year), parseInt(month), parseInt(day)); // Add time if provided if (hours && minutes) { date = date.set({ hour: parseInt(hours), minute: parseInt(minutes) }); } // Handle timezone if provided if (timezone) { // Convert timezone offset from ±HHMM to ±HH:MM format const formattedTz = timezone.replace(/([+-])(\d{2})(\d{2})/, '$1$2:$3'); date = date.setZone(formattedTz); } else if (preferences.timeZone) { // If no explicit timezone but preferences has one date = date.setZone(preferences.timeZone); } // Convert to UTC for storage const utcDate = date.toUTC(); if (!utcDate.isValid) { return null; } return createDateComponent(utcDate, { start: 0, end: fullMatch.length }, fullMatch, preferences); } }, { regex: /^(\d{1,2})\/(\d{1,2})\/(\d{2,4})(?:\s+(\d{1,2}):(\d{2}))?(?:\s*([+-]\d{4})?)?$/, parse: (matches, preferences) => { const [fullMatch, month, day, year, hours, minutes, timezone] = matches; let parsedYear = parseInt(year); // Handle 2-digit years if (parsedYear < 100) { parsedYear += parsedYear < 50 ? 2000 : 1900; } // Create base date in UTC let date = luxon_1.DateTime.utc(parsedYear, parseInt(month), parseInt(day)); // Add time if provided if (hours && minutes) { date = date.set({ hour: parseInt(hours), minute: parseInt(minutes) }); } // Handle timezone if provided if (timezone) { // Convert timezone offset from ±HHMM to ±HH:MM format const formattedTz = timezone.replace(/([+-])(\d{2})(\d{2})/, '$1$2:$3'); date = date.setZone(formattedTz); } else if (preferences.timeZone) { // If no explicit timezone but preferences has one date = date.setZone(preferences.timeZone); } // Convert to UTC for storage const utcDate = date.toUTC(); if (!utcDate.isValid) { return null; } return createDateComponent(utcDate, { start: 0, end: fullMatch.length }, fullMatch, preferences); } }, { regex: /^(\d{4})-(\d{2})-(\d{2})(?:\s+at\s+(\d{1,2}):(\d{2})(?:\s*(AM|PM))?)?$/i, parse: (matches, preferences) => { const [fullMatch, year, month, day, hours, minutes, meridiem] = matches; // Parse the time components let hour = hours ? parseInt(hours) : 0; const minute = minutes ? parseInt(minutes) : 0; if (meridiem) { if (hour > 12) return null; if (meridiem.toUpperCase() === 'PM' && hour < 12) hour += 12; if (meridiem.toUpperCase() === 'AM' && hour === 12) hour = 0; } // Create base date in UTC let date = luxon_1.DateTime.utc(parseInt(year), parseInt(month), parseInt(day), hour, minute); // Handle timezone if preferences has one if (preferences.timeZone) { date = date.setZone(preferences.timeZone); } // Convert to UTC for storage const utcDate = date.toUTC(); if (!utcDate.isValid) { return null; } return createDateComponent(utcDate, { start: 0, end: fullMatch.length }, fullMatch, preferences); } } ]; exports.absoluteDatesRule = { name: 'absolute-dates', patterns }; //# sourceMappingURL=absolute-dates.js.map