UNPKG

chrono-node

Version:

A natural language date parser in Javascript

331 lines 11.9 kB
import { Meridiem } from "../../types.js"; function primaryTimePattern(leftBoundary, primaryPrefix, primarySuffix, flags) { return new RegExp(`${leftBoundary}` + `${primaryPrefix}` + `(\\d{1,4})` + `(?:` + `(?:\\.|:|:)` + `(\\d{1,2})` + `(?:` + `(?::|:)` + `(\\d{2})` + `(?:\\.(\\d{1,6}))?` + `)?` + `)?` + `(?:\\s*(a\\.m\\.|p\\.m\\.|am?|pm?))?` + `${primarySuffix}`, flags); } function followingTimePatten(followingPhase, followingSuffix) { return new RegExp(`^(${followingPhase})` + `(\\d{1,4})` + `(?:` + `(?:\\.|\\:|\\:)` + `(\\d{1,2})` + `(?:` + `(?:\\.|\\:|\\:)` + `(\\d{1,2})(?:\\.(\\d{1,6}))?` + `)?` + `)?` + `(?:\\s*(a\\.m\\.|p\\.m\\.|am?|pm?))?` + `${followingSuffix}`, "i"); } const HOUR_GROUP = 2; const MINUTE_GROUP = 3; const SECOND_GROUP = 4; const MILLI_SECOND_GROUP = 5; const AM_PM_HOUR_GROUP = 6; export class AbstractTimeExpressionParser { constructor(strictMode = false) { this.cachedPrimaryPrefix = null; this.cachedPrimarySuffix = null; this.cachedPrimaryTimePattern = null; this.cachedFollowingPhase = null; this.cachedFollowingSuffix = null; this.cachedFollowingTimePatten = null; this.strictMode = strictMode; } patternFlags() { return "i"; } primaryPatternLeftBoundary() { return `(^|\\s|T|\\b)`; } primarySuffix() { return `(?!/)(?=\\W|$)`; } followingSuffix() { return `(?!/)(?=\\W|$)`; } pattern(context) { return this.getPrimaryTimePatternThroughCache(); } extract(context, match) { const startComponents = this.extractPrimaryTimeComponents(context, match); if (!startComponents) { if (match[0].match(/^\d{4}/)) { match.index += 4; return null; } match.index += match[0].length; return null; } const index = match.index + match[1].length; const text = match[0].substring(match[1].length); const result = context.createParsingResult(index, text, startComponents); match.index += match[0].length; const remainingText = context.text.substring(match.index); const followingPattern = this.getFollowingTimePatternThroughCache(); const followingMatch = followingPattern.exec(remainingText); if (text.match(/^\d{3,4}/) && followingMatch) { if (followingMatch[0].match(/^\s*([+-])\s*\d{2,4}$/)) { return null; } if (followingMatch[0].match(/^\s*([+-])\s*\d{2}\W\d{2}/)) { return null; } } if (!followingMatch || followingMatch[0].match(/^\s*([+-])\s*\d{3,4}$/)) { return this.checkAndReturnWithoutFollowingPattern(result); } result.end = this.extractFollowingTimeComponents(context, followingMatch, result); if (result.end) { result.text += followingMatch[0]; } return this.checkAndReturnWithFollowingPattern(result); } extractPrimaryTimeComponents(context, match, strict = false) { const components = context.createParsingComponents(); let minute = 0; let meridiem = null; let hour = parseInt(match[HOUR_GROUP]); if (hour > 100) { if (this.strictMode || match[MINUTE_GROUP] != null) { return null; } minute = hour % 100; hour = Math.floor(hour / 100); } if (hour > 24) { return null; } if (match[MINUTE_GROUP] != null) { if (match[MINUTE_GROUP].length == 1 && !match[AM_PM_HOUR_GROUP]) { return null; } minute = parseInt(match[MINUTE_GROUP]); } if (minute >= 60) { return null; } if (hour > 12) { meridiem = Meridiem.PM; } if (match[AM_PM_HOUR_GROUP] != null) { if (hour > 12) return null; const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase(); if (ampm == "a") { meridiem = Meridiem.AM; if (hour == 12) { hour = 0; } } if (ampm == "p") { meridiem = Meridiem.PM; if (hour != 12) { hour += 12; } } } components.assign("hour", hour); components.assign("minute", minute); if (meridiem !== null) { components.assign("meridiem", meridiem); } else { if (hour < 12) { components.imply("meridiem", Meridiem.AM); } else { components.imply("meridiem", Meridiem.PM); } } if (match[MILLI_SECOND_GROUP] != null) { const millisecond = parseInt(match[MILLI_SECOND_GROUP].substring(0, 3)); if (millisecond >= 1000) return null; components.assign("millisecond", millisecond); } if (match[SECOND_GROUP] != null) { const second = parseInt(match[SECOND_GROUP]); if (second >= 60) return null; components.assign("second", second); } return components; } extractFollowingTimeComponents(context, match, result) { const components = context.createParsingComponents(); if (match[MILLI_SECOND_GROUP] != null) { const millisecond = parseInt(match[MILLI_SECOND_GROUP].substring(0, 3)); if (millisecond >= 1000) return null; components.assign("millisecond", millisecond); } if (match[SECOND_GROUP] != null) { const second = parseInt(match[SECOND_GROUP]); if (second >= 60) return null; components.assign("second", second); } let hour = parseInt(match[HOUR_GROUP]); let minute = 0; let meridiem = -1; if (match[MINUTE_GROUP] != null) { minute = parseInt(match[MINUTE_GROUP]); } else if (hour > 100) { minute = hour % 100; hour = Math.floor(hour / 100); } if (minute >= 60 || hour > 24) { return null; } if (hour >= 12) { meridiem = Meridiem.PM; } if (match[AM_PM_HOUR_GROUP] != null) { if (hour > 12) { return null; } const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase(); if (ampm == "a") { meridiem = Meridiem.AM; if (hour == 12) { hour = 0; if (!components.isCertain("day")) { components.imply("day", components.get("day") + 1); } } } if (ampm == "p") { meridiem = Meridiem.PM; if (hour != 12) hour += 12; } if (!result.start.isCertain("meridiem")) { if (meridiem == Meridiem.AM) { result.start.imply("meridiem", Meridiem.AM); if (result.start.get("hour") == 12) { result.start.assign("hour", 0); } } else { result.start.imply("meridiem", Meridiem.PM); if (result.start.get("hour") != 12) { result.start.assign("hour", result.start.get("hour") + 12); } } } } components.assign("hour", hour); components.assign("minute", minute); if (meridiem >= 0) { components.assign("meridiem", meridiem); } else { const startAtPM = result.start.isCertain("meridiem") && result.start.get("hour") > 12; if (startAtPM) { if (result.start.get("hour") - 12 > hour) { components.imply("meridiem", Meridiem.AM); } else if (hour <= 12) { components.assign("hour", hour + 12); components.assign("meridiem", Meridiem.PM); } } else if (hour > 12) { components.imply("meridiem", Meridiem.PM); } else if (hour <= 12) { components.imply("meridiem", Meridiem.AM); } } if (components.date().getTime() < result.start.date().getTime()) { components.imply("day", components.get("day") + 1); } return components; } checkAndReturnWithoutFollowingPattern(result) { if (result.text.match(/^\d$/)) { return null; } if (result.text.match(/^\d\d\d+$/)) { return null; } if (result.text.match(/\d[apAP]$/)) { return null; } const endingWithNumbers = result.text.match(/[^\d:.](\d[\d.]+)$/); if (endingWithNumbers) { const endingNumbers = endingWithNumbers[1]; if (this.strictMode) { return null; } if (endingNumbers.includes(".") && !endingNumbers.match(/\d(\.\d{2})+$/)) { return null; } const endingNumberVal = parseInt(endingNumbers); if (endingNumberVal > 24) { return null; } } return result; } checkAndReturnWithFollowingPattern(result) { if (result.text.match(/^\d+-\d+$/)) { return null; } const endingWithNumbers = result.text.match(/[^\d:.](\d[\d.]+)\s*-\s*(\d[\d.]+)$/); if (endingWithNumbers) { if (this.strictMode) { return null; } const startingNumbers = endingWithNumbers[1]; const endingNumbers = endingWithNumbers[2]; if (endingNumbers.includes(".") && !endingNumbers.match(/\d(\.\d{2})+$/)) { return null; } const endingNumberVal = parseInt(endingNumbers); const startingNumberVal = parseInt(startingNumbers); if (endingNumberVal > 24 || startingNumberVal > 24) { return null; } } return result; } getPrimaryTimePatternThroughCache() { const primaryPrefix = this.primaryPrefix(); const primarySuffix = this.primarySuffix(); if (this.cachedPrimaryPrefix === primaryPrefix && this.cachedPrimarySuffix === primarySuffix) { return this.cachedPrimaryTimePattern; } this.cachedPrimaryTimePattern = primaryTimePattern(this.primaryPatternLeftBoundary(), primaryPrefix, primarySuffix, this.patternFlags()); this.cachedPrimaryPrefix = primaryPrefix; this.cachedPrimarySuffix = primarySuffix; return this.cachedPrimaryTimePattern; } getFollowingTimePatternThroughCache() { const followingPhase = this.followingPhase(); const followingSuffix = this.followingSuffix(); if (this.cachedFollowingPhase === followingPhase && this.cachedFollowingSuffix === followingSuffix) { return this.cachedFollowingTimePatten; } this.cachedFollowingTimePatten = followingTimePatten(followingPhase, followingSuffix); this.cachedFollowingPhase = followingPhase; this.cachedFollowingSuffix = followingSuffix; return this.cachedFollowingTimePatten; } } //# sourceMappingURL=AbstractTimeExpressionParser.js.map