UNPKG

tell-me-when

Version:
1 lines 91 kB
{"version":3,"file":"en-US.mjs","names":["GrammarNode","ParseNode","ParseRootNode","base","token","group","named","oneOf","longestOf","negativeLookahead","space","FullYearNode","constructor","wrapped","from","to","year","input","parseInt","substringOf","dateFns","FullYear","parseAs","TwoDigitYearNode","digits","replace","TwoDigitYear","YearNum","or","YearNumNotHour","MonthNumNode","month","afterNow","beforeNow","MonthNum","MonthNameNode","months","jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec","substring","toLowerCase","MonthNameFull","MonthNameAbbrev","MonthName","maybe","RelativeMonthNameNode","_this$find","find","Error","RelativeMonthName","MonthNameNoDot","Month","MonthNoDot","DayOfMonthNumNode","dayOfMonth","DayOfMonthNum","NthDayOfMonthNode","value","NthDayOfMonth","DayOfMonth","RelativeIntervalNode","intervalName","offset","RelativeSecondNode","RelativeMinuteNode","RelativeHourNode","RelativeWeekNode","RelativeMonthNode","RelativeYearNode","RelativeIntervalNodes","Second","Minute","Hour","Week","Year","RelativeIntervalBase","RegExp","RelativeInterval","RelativeSecond","RelativeMinute","RelativeHour","RelativeWeek","RelativeMonth","RelativeYear","DateNode","yearFns","_ref","_this$find2","filter","fn","monthFns","_ref2","undefined","relativeMonthFns","_this$find3","day","_ref3","relativeMonth","Date","DayDate","HoursNode","hours","Hours","MinutesNode","minutes","Minutes","SecondsNode","seconds","Seconds","MillisecondsNode","milliseconds","padEnd","Milliseconds","AmPmValue","AmPmNode","amPm","AM","PM","AmPm","TimeNode","_this$find4","_this$find5","_this$find6","_this$find7","_this$find8","AtTime","Time","NowNode","Now","QuantityNumNode","quantity","QuantityNum","QuantityWordNode","quantities","zero","an","a","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen","twenty","QuantityWord","Object","keys","join","QuantityNode","_ref4","Quantity","DateTimeUnitNode","unit","dateFnName","DateTimeUnit","DateTimeIntervalPartNode","_this$find9","_this$find10","DateTimeIntervalPart","DateTimeIntervalNode","fns","node","findAll","push","DateTimeInterval","repeat","Infinity","DateTimeOffsetNode","_this$find11","BeforeNow","AfterNow","DateTimeOffset","RangeEndDateTimeOffsetNode","RangeEndDateTimeOffset","RelativeDayNode","_this$find12","RelativeDayBase","RelativeDay","RangeEndRelativeDayNode","RangeEndRelativeDay","DayOfWeekNode","dayOfWeek","DayOfWeek","RelativeDayOfWeekNode","_this$find13","RelativeDayOfWeek","SpecificDay","RangeEndSpecificDay","RangeEndRelativeIntervalNode","RangeEndRelativeSecondNode","RangeEndRelativeMinuteNode","RangeEndRelativeHourNode","RangeEndRelativeWeekNode","RangeEndRelativeMonthNode","RangeEndRelativeYearNode","RangeEndRelativeIntervalNodes","RangeEndRelativeInterval","RangeEndRelativeSecond","RangeEndRelativeMinute","RangeEndRelativeHour","RangeEndRelativeWeek","RangeEndRelativeMonth","RangeEndRelativeYear","negateDateFns","map","DateTimeOffsetIntervalUnitNode","DateTimeOffsetIntervalUnit","DateTimeOffsetIntervalNode","_this$find14","_this$find15","RangeEndDateTimeOffsetIntervalNode","DateTimeOffsetIntervalBase","DateTimeOffsetInterval","RangeEndDateTimeOffsetInterval","DateTimeNode","date","_ref5","time","_this$find16","lastIfIndex","findIndex","op","index","startsWith","DateTime","RangeEndDateTime","RangeNode","_this$find17","_this$find18","_endFns","RangeStart","start","RangeEnd","end","some","f","through","endFns","flatMap","slice","length","pop","Range","RootNode","_ref6","Root","parse","grammar","tellMeWhen","when","options"],"sources":["src/en-US.ts"],"sourcesContent":["import { AddFn, DateFn } from './util/DateFn'\nimport { GrammarNode } from './util/GrammarNode'\nimport { ParseNode } from './util/ParseNode'\nimport { ParseRootNode } from './util/ParseRootNode'\nimport * as base from './util/parse'\n\nconst { token, group, named, oneOf, longestOf, negativeLookahead } = GrammarNode\n\nexport const space = token(/\\s+/)\n\nexport class FullYearNode extends ParseNode {\n constructor(public wrapped: ParseNode) {\n super('FullYear', wrapped.from, wrapped.to)\n }\n\n year(input: string) {\n return parseInt(this.substringOf(input))\n }\n\n dateFns(input: string): DateFn[] {\n return [['setYear', this.year(input)]]\n }\n}\nconst FullYear = token(/\\d{4}/).parseAs(FullYearNode)\n\nexport class TwoDigitYearNode extends ParseNode {\n constructor(public wrapped: ParseNode) {\n super('TwoDigitYear', wrapped.from, wrapped.to)\n }\n\n year(input: string) {\n const digits = parseInt(this.substringOf(input).replace(/^'/, ''))\n return digits >= 70 ? 1900 + digits : 2000 + digits\n }\n\n dateFns(input: string): DateFn[] {\n return [['setYear', this.year(input)]]\n }\n}\nconst TwoDigitYear = token(/'?\\d\\d/).parseAs(TwoDigitYearNode)\n\nconst YearNum = FullYear.or(TwoDigitYear)\n\nconst YearNumNotHour = oneOf(\n FullYear,\n token(/'\\d\\d/).parseAs(TwoDigitYearNode),\n group(\n token(/\\d\\d/).parseAs(TwoDigitYearNode),\n negativeLookahead(/:|\\s*[ap](m|\\s)/i)\n )\n)\n\nexport class MonthNumNode extends ParseNode {\n constructor(public wrapped: ParseNode) {\n super('MonthNum', wrapped.from, wrapped.to)\n }\n\n month(input: string) {\n return parseInt(this.substringOf(input)) - 1\n }\n\n dateFns(input: string): DateFn[] {\n return [\n ['setMonth', this.month(input)],\n [\n 'closestToNow',\n [['if', { afterNow: [['addYears', -1]] }]],\n [['if', { beforeNow: [['addYears', 1]] }]],\n ],\n ['startOfMonth'],\n ['makeInterval', ['addMonths', 1]],\n ]\n }\n}\nconst MonthNum = token(/1[0-2]|0?[1-9]/).parseAs(MonthNumNode)\n\nexport class MonthNameNode extends ParseNode {\n static months = {\n jan: 0,\n feb: 1,\n mar: 2,\n apr: 3,\n may: 4,\n jun: 5,\n jul: 6,\n aug: 7,\n sep: 8,\n oct: 9,\n nov: 10,\n dec: 11,\n }\n\n month(input: string) {\n return MonthNameNode.months[\n input\n .substring(this.from, this.from + 3)\n .toLowerCase() as keyof (typeof MonthNameNode)['months']\n ]\n }\n\n dateFns(input: string): DateFn[] {\n const month = this.month(input)\n return [\n ['setMonth', month],\n [\n 'closestToNow',\n [['if', { afterNow: [['addYears', -1]] }]],\n [['if', { beforeNow: [['addYears', 1]] }]],\n ],\n ['startOfMonth'],\n ['makeInterval', ['addMonths', 1]],\n ]\n }\n}\nconst MonthNameFull = named(\n 'MonthNameFull',\n /(january|february|march|may|april|june|july|august|september|october|november|december)(?![a-z])/i\n).parseAs(MonthNameNode)\n\nconst MonthNameAbbrev = named(\n 'MonthNameAbbrev',\n /(jan|feb|mar|apr|may|jun|jul|aug|sept?|oct|nov|dec)(?![a-z])/i\n).parseAs(MonthNameNode)\n\nconst MonthName = MonthNameFull.or(group(MonthNameAbbrev, group('.').maybe()))\n\nexport class RelativeMonthNameNode extends ParseNode {\n dateFns(input: string): DateFn[] {\n const month = this.find(MonthNameNode)?.month(input)\n if (month == null) throw new Error(`failed to find month name node`)\n\n if (this.find('Next')) {\n return [\n ['setMonth', month],\n ['startOfMonth'],\n ['if', { beforeNow: [['addYears', 1]] }],\n ['makeInterval', ['addMonths', 1]],\n ]\n }\n if (this.find('AfterNext')) {\n return [\n ['setMonth', month],\n ['startOfMonth'],\n ['if', { beforeNow: [['addYears', 1]] }],\n ['addYears', 1],\n ['makeInterval', ['addMonths', 1]],\n ]\n }\n if (this.find('Last')) {\n return [\n ['setMonth', month],\n ['startOfMonth'],\n ['addMonths', 1],\n ['if', { afterNow: [['addYears', -1]] }],\n ['addMonths', -1],\n ['makeInterval', ['addMonths', 1]],\n ]\n }\n if (this.find('BeforeLast')) {\n return [\n ['setMonth', month],\n ['startOfMonth'],\n ['addMonths', 1],\n ['if', { afterNow: [['addYears', -1]] }],\n ['addMonths', -1],\n ['addYears', -1],\n ['makeInterval', ['addMonths', 1]],\n ]\n }\n return []\n }\n}\n\nexport const RelativeMonthName = named(\n 'RelativeMonthName',\n oneOf(\n group(named('Last', /last/i), space, MonthName),\n group(named('Next', /next/i), space, MonthName),\n group(MonthName, space, named('BeforeLast', /before\\s+last/i)),\n group(MonthName, space, named('AfterNext', /after\\s+next/i))\n )\n).parseAs(RelativeMonthNameNode)\n\nconst MonthNameNoDot = MonthNameFull.or(MonthNameAbbrev)\n\nconst Month = MonthName.or(MonthNum)\nconst MonthNoDot = MonthNameNoDot.or(MonthNum)\n\nexport class DayOfMonthNumNode extends ParseNode {\n constructor(public wrapped: ParseNode) {\n super('DayOfMonthNum', wrapped.from, wrapped.to)\n }\n\n dayOfMonth(input: string) {\n return parseInt(this.substringOf(input))\n }\n}\nconst DayOfMonthNum = token(/[12][0-9]|3[01]|0?[1-9]/).parseAs(\n DayOfMonthNumNode\n)\n\nexport class NthDayOfMonthNode extends ParseNode {\n constructor(public wrapped: ParseNode) {\n super('NthDayOfMonth', wrapped.from, wrapped.to)\n }\n\n dayOfMonth(input: string) {\n const value = this.substringOf(input).toLowerCase()\n switch (value) {\n case '1st':\n case 'first':\n return 1\n case '2nd':\n case 'second':\n return 2\n case '3rd':\n case 'third':\n return 3\n case '4th':\n case 'fourth':\n return 4\n case '5th':\n case 'fifth':\n return 5\n case '6th':\n case 'sixth':\n return 6\n case '7th':\n case 'seventh':\n return 7\n case '8th':\n case 'eighth':\n return 8\n case '9th':\n case 'ninth':\n return 9\n case '10th':\n case 'tenth':\n return 10\n case '11th':\n case 'eleventh':\n return 11\n case '12th':\n case 'twelfth':\n return 12\n case '13th':\n case 'thirteenth':\n return 13\n case '14th':\n case 'fourteenth':\n return 14\n case '15th':\n case 'fifteenth':\n return 15\n case '16th':\n case 'sixteenth':\n return 16\n case '17th':\n case 'seventeenth':\n return 17\n case '18th':\n case 'eighteenth':\n return 18\n case '19th':\n case 'ninteenth':\n return 19\n case '20th':\n case 'twentieth':\n return 20\n case '21st':\n case 'twenty-first':\n return 21\n case '22nd':\n case 'twenty-second':\n return 22\n case '23rd':\n case 'twenty-third':\n return 23\n case '24th':\n case 'twenty-fourth':\n return 24\n case '25th':\n case 'twenty-fifth':\n return 25\n case '26th':\n case 'twenty-sixth':\n return 26\n case '27th':\n case 'twenty-seventh':\n return 27\n case '28th':\n case 'twenty-eighth':\n return 28\n case '29th':\n case 'twenty-ninthy':\n return 29\n case '30th':\n case 'thirtieth':\n return 30\n case '31st':\n case 'thirty-first':\n return 31\n }\n }\n}\nconst NthDayOfMonth = token(\n /(1st|first|2nd|second|3rd|third|4th|fourth|5th|fifth|6th|sixth|7th|seventh|8th|eighth|9th|ninth|10th|tenth|11th|eleventh|12th|twelfth|13th|thirteenth|14th|fourteenth|15th|fifteenth|16th|sixteenth|17th|seventeenth|18th|eighteenth|19th|ninteenth|20th|twentieth|21st|twenty-first|22nd|twenty-second|23rd|twenty-third|24th|twenty-fourth|25th|twenty-fifth|26th|twenty-sixth|27th|twenty-seventh|28th|twenty-eighth|29th|twenty-ninthy|30th|thirtieth|31st|thirty-first)(?![a-z])/i\n).parseAs(NthDayOfMonthNode)\n\nconst DayOfMonth = NthDayOfMonth.or(DayOfMonthNum)\n\ntype RelativeIntervalType =\n | 'Second'\n | 'Minute'\n | 'Day'\n | 'Hour'\n | 'Week'\n | 'Month'\n | 'Year'\n\nexport abstract class RelativeIntervalNode extends ParseNode {\n abstract get intervalName(): RelativeIntervalType\n\n dateFns(): DateFn[] {\n const { intervalName } = this\n\n const offset = this.find(`Next${intervalName}`)\n ? 1\n : this.find(`Last${intervalName}`)\n ? -1\n : this.find(`${intervalName}BeforeLast`)\n ? -2\n : this.find(`${intervalName}AfterNext`)\n ? 2\n : 0\n\n return [\n ...(offset ? ([[`add${intervalName}s`, offset]] as DateFn[]) : []),\n [`startOf${intervalName}`],\n [`makeInterval`, [`add${intervalName}s`, 1]],\n ]\n }\n}\n\nexport class RelativeSecondNode extends RelativeIntervalNode {\n get intervalName(): 'Second' {\n return 'Second'\n }\n}\nexport class RelativeMinuteNode extends RelativeIntervalNode {\n get intervalName(): 'Minute' {\n return 'Minute'\n }\n}\nexport class RelativeHourNode extends RelativeIntervalNode {\n get intervalName(): 'Hour' {\n return 'Hour'\n }\n}\nexport class RelativeWeekNode extends RelativeIntervalNode {\n get intervalName(): 'Week' {\n return 'Week'\n }\n}\nexport class RelativeMonthNode extends RelativeIntervalNode {\n get intervalName(): 'Month' {\n return 'Month'\n }\n}\nexport class RelativeYearNode extends RelativeIntervalNode {\n get intervalName(): 'Year' {\n return 'Year'\n }\n}\n\nconst RelativeIntervalNodes = {\n Second: RelativeSecondNode,\n Minute: RelativeMinuteNode,\n Hour: RelativeHourNode,\n Week: RelativeWeekNode,\n Month: RelativeMonthNode,\n Year: RelativeYearNode,\n}\n\nconst RelativeIntervalBase = (intervalName: RelativeIntervalType) =>\n oneOf(\n named(`This${intervalName}`, new RegExp(`this\\\\s+${intervalName}`, 'i')),\n named(`Last${intervalName}`, new RegExp(`last\\\\s+${intervalName}`, 'i')),\n named(`Next${intervalName}`, new RegExp(`next\\\\s+${intervalName}`, 'i')),\n named(\n `${intervalName}BeforeLast`,\n new RegExp(`(the\\\\s+)?${intervalName}\\\\s+before\\\\s+last`, 'i')\n ),\n named(\n `${intervalName}AfterNext`,\n new RegExp(`(the\\\\s+)?${intervalName}\\\\s+after\\\\s+next`, 'i')\n )\n )\n\nconst RelativeInterval = (intervalName: Exclude<RelativeIntervalType, 'Day'>) =>\n named(`Relative${intervalName}`, RelativeIntervalBase(intervalName)).parseAs(\n RelativeIntervalNodes[intervalName]\n )\n\nexport const RelativeSecond = RelativeInterval('Second')\nexport const RelativeMinute = RelativeInterval('Minute')\nexport const RelativeHour = RelativeInterval('Hour')\nexport const RelativeWeek = RelativeInterval('Week')\nexport const RelativeMonth = RelativeInterval('Month')\nexport const RelativeYear = RelativeInterval('Year')\n\nexport class DateNode extends ParseNode {\n yearFns(input: string): DateFn[] | undefined {\n return (\n (this.find(FullYearNode) || this.find(TwoDigitYearNode))?.dateFns(\n input\n ) ||\n this.find(RelativeYearNode)\n ?.dateFns()\n .filter((fn) => fn[0] === 'addYears')\n )\n }\n monthFns(input: string): DateFn[] | undefined {\n const month = (\n this?.find(MonthNumNode) || this?.find(MonthNameNode)\n )?.month(input)\n return month != null ? [['setMonth', month]] : undefined\n }\n relativeMonthFns(input: string): DateFn[] | undefined {\n return this.find(RelativeMonthNameNode)\n ?.dateFns(input)\n .filter((fn) => fn[0] !== 'makeInterval')\n }\n day(input: string) {\n return (\n this?.find(DayOfMonthNumNode) || this?.find(NthDayOfMonthNode)\n )?.dayOfMonth(input)\n }\n\n dateFns(input: string): DateFn[] {\n const year = this.yearFns(input)\n const relativeMonth = this.relativeMonthFns(input)\n const month = relativeMonth || this.monthFns(input)\n const day = this.day(input)\n\n if (year == null) {\n return [\n ...(month || []),\n ...(day != null ? ([['setDate', day]] satisfies DateFn[]) : []),\n ...((relativeMonth\n ? day != null\n ? [['startOfDay']]\n : []\n : [\n [\n day != null\n ? 'startOfDay'\n : month != null\n ? 'startOfMonth'\n : 'startOfYear',\n ],\n [\n 'closestToNow',\n [\n [\n 'if',\n {\n afterNow: [\n [month != null ? 'addYears' : 'addMonths', -1],\n ],\n },\n ],\n ],\n [\n [\n 'if',\n {\n beforeNow: [\n [month != null ? 'addYears' : 'addMonths', 1],\n ],\n },\n ],\n ],\n ],\n ]) satisfies DateFn[]),\n ['makeInterval', [day != null ? 'addDays' : 'addMonths', 1]],\n ]\n }\n\n return [\n ...(year || []),\n ...(month || []),\n ...(day != null ? ([['setDate', day]] as DateFn[]) : []),\n [\n day != null\n ? 'startOfDay'\n : month != null\n ? 'startOfMonth'\n : 'startOfYear',\n ],\n [\n 'makeInterval',\n [day != null ? 'addDays' : month != null ? 'addMonths' : 'addYears', 1],\n ],\n ]\n }\n}\n\nconst Date = named(\n 'Date',\n longestOf(\n group(\n FullYear,\n oneOf(\n group(MonthNameNoDot, DayOfMonth.maybe()),\n group('.', MonthNoDot, group('.', DayOfMonth).maybe()),\n group('-', MonthNoDot, group('-', DayOfMonth).maybe()),\n group('_', MonthNoDot, group('_', DayOfMonth).maybe()),\n group('/', MonthNoDot, group('/', DayOfMonth).maybe()),\n group(space, Month, group(space, DayOfMonth).maybe())\n ).maybe()\n ),\n group(\n RelativeYear,\n group(space, Month, group(space, DayOfMonth).maybe()).maybe()\n ),\n group(\n MonthName,\n longestOf(\n group(\n space,\n DayOfMonth,\n group(\n space.or(group(space.maybe(), ',', space.maybe())),\n oneOf(YearNumNotHour, RelativeYear)\n ).maybe()\n ),\n group(\n space.or(group(space.maybe(), ',', space.maybe())),\n oneOf(YearNum, RelativeYear)\n )\n ).maybe()\n ),\n group(RelativeMonthName, group(space, DayOfMonth).maybe()),\n group(\n MonthNameNoDot,\n oneOf(\n group('.', DayOfMonth, group('.', YearNum).maybe()),\n group(NthDayOfMonth, YearNumNotHour.maybe()),\n DayOfMonth,\n group('-', DayOfMonth, group('-', YearNum).maybe()),\n group('_', DayOfMonth, group('_', YearNum).maybe()),\n group('/', DayOfMonth, group('/', YearNum).maybe())\n ).maybe()\n ),\n group(\n MonthNum,\n longestOf(\n group(/[- ._/]/, FullYear),\n group(NthDayOfMonth, YearNumNotHour.maybe()),\n group('.', DayOfMonth, group('.', YearNum).maybe()),\n group('-', DayOfMonth, group('-', YearNum).maybe()),\n group('_', DayOfMonth, group('_', YearNum).maybe()),\n group('/', DayOfMonth, group('/', YearNum).maybe()),\n group(space, DayOfMonth, group(space, YearNumNotHour).maybe())\n )\n ),\n group(\n group('the', space).maybe(),\n NthDayOfMonth,\n oneOf(\n group(MonthNameNoDot, YearNumNotHour.maybe()),\n group('.', MonthNoDot, group('.', YearNum).maybe()),\n group('-', MonthNoDot, group('-', YearNum).maybe()),\n group('_', MonthNoDot, group('_', YearNum).maybe()),\n group('/', MonthNoDot, group('/', YearNum).maybe()),\n group(\n space,\n group(group('day', space).maybe(), 'of', space).maybe(),\n MonthName,\n group(\n space.or(group(space.maybe(), ',', space.maybe())),\n oneOf(YearNumNotHour, RelativeYear)\n ).maybe()\n )\n ).maybe()\n ),\n group(\n DayOfMonthNum,\n oneOf(\n group(MonthNameNoDot, YearNumNotHour.maybe()),\n group('.', MonthNoDot, group('.', YearNum).maybe()),\n group('-', MonthNoDot, group('-', YearNum).maybe()),\n group('_', MonthNoDot, group('_', YearNum).maybe()),\n group('/', MonthNoDot, group('/', YearNum).maybe()),\n group(space, MonthName, group(space, RelativeYear).maybe()),\n group(space, Month, group(space, YearNumNotHour).maybe())\n )\n )\n )\n).parseAs(DateNode)\n\nconst DayDate = named(\n 'DayDate',\n longestOf(\n group(\n FullYear,\n oneOf(\n group(MonthNameNoDot, DayOfMonth),\n group('.', MonthNoDot, group('.', DayOfMonth)),\n group('-', MonthNoDot, group('-', DayOfMonth)),\n group('_', MonthNoDot, group('_', DayOfMonth)),\n group('/', MonthNoDot, group('/', DayOfMonth)),\n group(space, Month, group(space, DayOfMonth))\n ).maybe()\n ),\n group(RelativeYear, space, Month, group(space, DayOfMonth)),\n group(\n MonthName,\n group(\n space,\n DayOfMonth,\n group(\n space.or(group(space.maybe(), ',', space.maybe())),\n oneOf(YearNumNotHour, RelativeYear)\n ).maybe()\n )\n ),\n group(RelativeMonthName, group(space, DayOfMonth)),\n group(\n MonthNameNoDot,\n oneOf(\n group('.', DayOfMonth, group('.', YearNum).maybe()),\n group(NthDayOfMonth, YearNumNotHour.maybe()),\n DayOfMonth,\n group('-', DayOfMonth, group('-', YearNum).maybe()),\n group('_', DayOfMonth, group('_', YearNum).maybe()),\n group('/', DayOfMonth, group('/', YearNum).maybe())\n )\n ),\n group(\n MonthNum,\n oneOf(\n group(NthDayOfMonth, YearNumNotHour.maybe()),\n group('.', DayOfMonth, group('.', YearNum).maybe()),\n group('-', DayOfMonth, group('-', YearNum).maybe()),\n group('_', DayOfMonth, group('_', YearNum).maybe()),\n group('/', DayOfMonth, group('/', YearNum).maybe()),\n group(\n space,\n DayOfMonth,\n group(space, oneOf(YearNumNotHour, RelativeYear)).maybe()\n )\n )\n ),\n group(\n group('the', space).maybe(),\n NthDayOfMonth,\n oneOf(\n group(MonthNameNoDot, YearNumNotHour.maybe()),\n group('.', MonthNoDot, group('.', YearNum).maybe()),\n group('-', MonthNoDot, group('-', YearNum).maybe()),\n group('_', MonthNoDot, group('_', YearNum).maybe()),\n group('/', MonthNoDot, group('/', YearNum).maybe()),\n group(\n space,\n group(group('day', space).maybe(), 'of', space).maybe(),\n MonthName,\n group(\n space.or(group(space.maybe(), ',', space.maybe())),\n oneOf(YearNumNotHour, RelativeYear)\n ).maybe()\n )\n ).maybe()\n ),\n group(\n DayOfMonthNum,\n oneOf(\n group(MonthNameNoDot, YearNumNotHour.maybe()),\n group('.', MonthNoDot, group('.', YearNum).maybe()),\n group('-', MonthNoDot, group('-', YearNum).maybe()),\n group('_', MonthNoDot, group('_', YearNum).maybe()),\n group('/', MonthNoDot, group('/', YearNum).maybe()),\n group(\n space,\n Month,\n group(space, oneOf(YearNumNotHour, RelativeYear)).maybe()\n )\n )\n )\n )\n).parseAs(DateNode)\n\nexport class HoursNode extends ParseNode {\n hours(input: string) {\n return parseInt(this.substringOf(input))\n }\n}\nconst Hours = named('Hours', /2[0-3]|[01]?[0-9]/).parseAs(HoursNode)\nexport class MinutesNode extends ParseNode {\n minutes(input: string) {\n return parseInt(this.substringOf(input))\n }\n}\nconst Minutes = named('Minutes', /[0-5][0-9]/).parseAs(MinutesNode)\nexport class SecondsNode extends ParseNode {\n seconds(input: string) {\n return parseInt(this.substringOf(input))\n }\n}\nconst Seconds = named('Seconds', /[0-5][0-9]/).parseAs(SecondsNode)\nexport class MillisecondsNode extends ParseNode {\n milliseconds(input: string) {\n return parseInt(this.substringOf(input).padEnd(3, '0'))\n }\n}\nconst Milliseconds = named('Milliseconds', /\\d{1,3}/).parseAs(MillisecondsNode)\n\nexport enum AmPmValue {\n AM,\n PM,\n}\nexport class AmPmNode extends ParseNode {\n amPm(input: string) {\n switch (input.substring(this.from, this.from + 1)) {\n case 'a':\n case 'A':\n return AmPmValue.AM\n case 'p':\n case 'P':\n return AmPmValue.PM\n default:\n throw new Error(`unexpected`)\n }\n }\n}\nconst AmPm = named('AmPm', /[ap]m?(?!\\w)/i).parseAs(AmPmNode)\n\nexport class TimeNode extends ParseNode {\n hours(input: string) {\n return this.find(HoursNode)?.hours(input)\n }\n minutes(input: string) {\n return this.find(MinutesNode)?.minutes(input)\n }\n seconds(input: string) {\n return this.find(SecondsNode)?.seconds(input)\n }\n milliseconds(input: string) {\n return this.find(MillisecondsNode)?.milliseconds(input)\n }\n amPm(input: string) {\n return this.find(AmPmNode)?.amPm(input)\n }\n\n dateFns(input: string): DateFn[] {\n let hours = this.hours(input)\n const minutes = this.minutes(input)\n const seconds = this.seconds(input)\n const milliseconds = this.milliseconds(input)\n const amPm = this.amPm(input)\n\n if (hours != null && amPm != null) {\n if (hours < 0 || hours > 12) {\n throw new Error('hour out of range')\n }\n if (amPm === AmPmValue.PM && hours !== 12) hours += 12\n else if (amPm === AmPmValue.AM && hours === 12) hours = 0\n }\n\n return [\n ...(hours != undefined ? ([['setHours', hours]] as DateFn[]) : []),\n ...(minutes != undefined ? ([['setMinutes', minutes]] as DateFn[]) : []),\n ...(seconds != undefined ? ([['setSeconds', seconds]] as DateFn[]) : []),\n ...(milliseconds != undefined\n ? ([['setMilliseconds', milliseconds]] as DateFn[])\n : ([\n [\n seconds != undefined\n ? 'startOfSecond'\n : minutes != undefined\n ? 'startOfMinute'\n : 'startOfHour',\n ],\n ] as DateFn[])),\n ]\n }\n}\n\nconst AtTime = named(\n 'AtTime',\n Hours,\n group(\n ':',\n Minutes,\n group(':', Seconds, group('.', Milliseconds).maybe()).maybe()\n ).maybe(),\n group(space.maybe(), AmPm).maybe()\n).parseAs(TimeNode)\n\nconst Time = named(\n 'Time',\n longestOf(\n group(\n Hours,\n group(\n ':',\n Minutes,\n group(':', Seconds, group('.', Milliseconds).maybe()).maybe()\n ),\n group(space.maybe(), AmPm).maybe()\n ),\n group(\n Hours,\n group(\n ':',\n Minutes,\n group(':', Seconds, group('.', Milliseconds).maybe()).maybe()\n ).maybe(),\n group(space.maybe(), AmPm)\n )\n )\n).parseAs(TimeNode)\n\nexport class NowNode extends ParseNode {\n dateFns(): DateFn[] {\n return [['now']]\n }\n}\n\nconst Now = named('Now', /now|(the\\s+)?present(\\s+time)?/).parseAs(NowNode)\n\nexport class QuantityNumNode extends ParseNode {\n quantity(input: string) {\n return parseInt(this.substringOf(input))\n }\n}\n\nexport const QuantityNum = named('QuantityNum', /\\d+/).parseAs(QuantityNumNode)\n\nexport class QuantityWordNode extends ParseNode {\n static quantities = {\n zero: 0,\n an: 1,\n a: 1,\n one: 1,\n two: 2,\n three: 3,\n four: 4,\n five: 5,\n six: 6,\n seven: 7,\n eight: 8,\n nine: 9,\n ten: 10,\n eleven: 11,\n twelve: 12,\n thirteen: 13,\n fourteen: 14,\n fifteen: 15,\n sixteen: 16,\n seventeen: 17,\n eighteen: 18,\n nineteen: 19,\n twenty: 20,\n }\n quantity(input: string) {\n return QuantityWordNode.quantities[\n this.substringOf(\n input\n ).toLowerCase() as keyof typeof QuantityWordNode.quantities\n ]\n }\n}\n\nexport const QuantityWord = named(\n 'QuantityWord',\n new RegExp(Object.keys(QuantityWordNode.quantities).join('|'), 'i')\n).parseAs(QuantityWordNode)\n\nexport class QuantityNode extends ParseNode {\n quantity(input: string) {\n return (\n this.find(QuantityNumNode) || this.find(QuantityWordNode)\n )?.quantity(input)\n }\n}\n\nexport const Quantity = named(\n 'Quantity',\n oneOf(QuantityNum, QuantityWord)\n).parseAs(QuantityNode)\n\ntype DateTimeUnit =\n | 'years'\n | 'months'\n | 'weeks'\n | 'days'\n | 'hours'\n | 'minutes'\n | 'seconds'\n | 'milliseconds'\n\nexport class DateTimeUnitNode extends ParseNode {\n unit(input: string): DateTimeUnit {\n switch (this.substringOf(input).toLowerCase()) {\n case 'y':\n case 'yr':\n case 'year':\n case 'years':\n return 'years'\n case 'mo':\n case 'mos':\n case 'month':\n case 'months':\n return 'months'\n case 'w':\n case 'wk':\n case 'wks':\n case 'week':\n case 'weeks':\n return 'weeks'\n case 'd':\n case 'day':\n case 'days':\n return 'days'\n case 'h':\n case 'hr':\n case 'hrs':\n case 'hour':\n case 'hours':\n return 'hours'\n case 'm':\n case 'min':\n case 'mins':\n case 'minute':\n case 'minutes':\n return 'minutes'\n case 's':\n case 'sec':\n case 'secs':\n case 'second':\n case 'seconds':\n return 'seconds'\n case 'ms':\n case 'milli':\n case 'millis':\n case 'millisecond':\n case 'milliseconds':\n return 'milliseconds'\n default:\n throw new Error('unexpected')\n }\n }\n dateFnName(input: string): DateFn[0] {\n switch (this.unit(input)) {\n case 'years':\n return 'addYears'\n case 'months':\n return 'addMonths'\n case 'weeks':\n return 'addWeeks'\n case 'days':\n return 'addDays'\n case 'hours':\n return 'addHours'\n case 'minutes':\n return 'addMinutes'\n case 'seconds':\n return 'addSeconds'\n case 'milliseconds':\n return 'addMilliseconds'\n default:\n throw new Error('unexpected')\n }\n }\n}\nexport const DateTimeUnit = named(\n 'DateTimeUnit',\n /years?|yrs?|y|months?|mos?|weeks?|wks?|w|days?|d|hours?|hrs?|h|minutes?|mins?|m|seconds?|secs?|s|milliseconds?|millis?|ms/i\n).parseAs(DateTimeUnitNode)\n\nexport class DateTimeIntervalPartNode extends ParseNode {\n dateFns(input: string): AddFn[] {\n const quantity = this.find(QuantityNode)?.quantity(input)\n const dateFnName = this.find(DateTimeUnitNode)?.dateFnName(input)\n if (quantity == null || dateFnName == null) throw new Error(`unexpected`)\n return [[dateFnName, quantity]] as any\n }\n}\nexport const DateTimeIntervalPart = named(\n 'DateTimeIntervalPart',\n Quantity,\n space.maybe(),\n DateTimeUnit\n).parseAs(DateTimeIntervalPartNode)\n\ntype DateTimeInterval = { [U in DateTimeUnit]?: number }\n\nexport class DateTimeIntervalNode extends ParseNode {\n dateFns(input: string): AddFn[] {\n const fns: AddFn[] = []\n for (const node of this.findAll(DateTimeIntervalPartNode)) {\n fns.push(...node.dateFns(input))\n }\n return fns\n }\n}\n\nconst DateTimeInterval = named(\n 'DateTimeInterval',\n DateTimeIntervalPart,\n group(\n space.maybe(),\n group(',', space.maybe()).maybe(),\n group('and', space).maybe(),\n DateTimeIntervalPart\n ).repeat(0, Infinity)\n).parseAs(DateTimeIntervalNode)\n\nexport class DateTimeOffsetNode extends ParseNode {\n dateFns(input: string): DateFn[] {\n const fns: AddFn[] = this.find(DateTimeIntervalNode)?.dateFns(input) || []\n if (this.find('BeforeNow')) {\n for (const fn of fns) fn[1] = -fn[1]\n }\n return fns\n }\n}\n\nexport const BeforeNow = named(\n 'BeforeNow',\n oneOf('ago', /in\\s+the\\s+past/i, group('before', space, Now))\n)\n\nexport const AfterNow = named(\n 'AfterNow',\n oneOf(group(/after|from/i, space, Now), /in\\s+the\\s+future/i)\n)\n\nexport const DateTimeOffset = named(\n 'DateTimeOffset',\n DateTimeInterval,\n space.maybe(),\n oneOf(BeforeNow, AfterNow)\n).parseAs(DateTimeOffsetNode)\n\nexport class RangeEndDateTimeOffsetNode extends DateTimeOffsetNode {\n dateFns(input: string): DateFn[] {\n const fns: DateFn[] = super.dateFns(input)\n if (this.find('Later')) return fns\n return [['now'], ...fns]\n }\n}\nexport const RangeEndDateTimeOffset = named(\n 'RangeEndDateTimeOffset',\n DateTimeInterval,\n space.maybe(),\n oneOf(\n BeforeNow,\n AfterNow,\n named('Later', /after\\s+(then|that)|thereafter|later/)\n )\n).parseAs(RangeEndDateTimeOffsetNode)\n\nexport class RelativeDayNode extends ParseNode {\n dateFns(input: string): DateFn[] {\n const quantity = this.find(QuantityNode)?.quantity(input)\n if (quantity != null) {\n return [\n ['addDays', this.find('BeforeNow') ? -quantity : quantity],\n ] as DateFn[]\n }\n\n const offset = this.find('Tomorrow')\n ? 1\n : this.find('Yesterday')\n ? -1\n : this.find('DayBeforeYesterday')\n ? -2\n : this.find('DayAfterTomorrow')\n ? 2\n : 0\n\n return [\n ...(offset ? ([['addDays', offset]] as DateFn[]) : []),\n ['startOfDay'],\n ['makeInterval', ['addDays', 1]],\n ]\n }\n}\n\nconst RelativeDayBase = oneOf(\n named('Today', group('today')),\n named('Yesterday', group('yesterday')),\n named('Tomorrow', group('tomorrow')),\n group(\n group('the', space).maybe(),\n group('day', space),\n oneOf(\n named('DayBeforeYesterday', group('before', space, /yesterday|last/)),\n named('DayAfterTomorrow', group('after', space, /tomorrow|next/))\n )\n )\n)\n\nexport const RelativeDay = named('RelativeDay', RelativeDayBase).parseAs(\n RelativeDayNode\n)\n\nexport class RangeEndRelativeDayNode extends RelativeDayNode {\n dateFns(input: string): DateFn[] {\n return [['now'], ...super.dateFns(input)]\n }\n}\n\nexport const RangeEndRelativeDay = named(\n 'RangeEndRelativeDay',\n RelativeDayBase\n).parseAs(RangeEndRelativeDayNode)\n\nexport class DayOfWeekNode extends ParseNode {\n dayOfWeek(input: string): number {\n switch (this.substringOf(input).toLowerCase()) {\n case 'sunday':\n case 'sun':\n return 0\n case 'monday':\n case 'mon':\n return 1\n case 'tuesday':\n case 'tues':\n case 'tue':\n return 2\n case 'wednesday':\n case 'wed':\n return 3\n case 'thursday':\n case 'thurs':\n case 'thur':\n case 'thu':\n return 4\n case 'friday':\n case 'fri':\n return 5\n case 'saturday':\n case 'sat':\n return 6\n }\n throw new Error(`invalid day of week: ${this.substringOf(input)}`)\n }\n\n dateFns(input: string): DateFn[] {\n const dayOfWeek = this.dayOfWeek(input)\n\n return [\n ['setDay', dayOfWeek],\n [\n 'closestToNow',\n [['if', { afterNow: [['addWeeks', -1]] }]],\n [['if', { beforeNow: [['addWeeks', 1]] }]],\n ],\n ['startOfDay'],\n ['makeInterval', ['addDays', 1]],\n ]\n }\n}\n\nexport const DayOfWeek = named(\n 'DayOfWeek',\n /sun(day)?|tue(s(day)?)?|wed(nesday)?|thu(r(s(day)?)?)?|fri(day)?|sat(urday)?/i\n).parseAs(DayOfWeekNode)\n\nexport class RelativeDayOfWeekNode extends ParseNode {\n dateFns(input: string): DateFn[] {\n const dayOfWeek = this.find(DayOfWeekNode)?.dayOfWeek(input)\n if (dayOfWeek == null) throw new Error(`failed to find DayOfWeekNode`)\n if (this.find('Next')) {\n return [\n ['setDay', dayOfWeek],\n ['startOfDay'],\n ['if', { beforeNow: [['addWeeks', 1]] }],\n ['makeInterval', ['addDays', 1]],\n ]\n }\n if (this.find('AfterNext')) {\n return [\n ['setDay', dayOfWeek],\n ['startOfDay'],\n ['if', { beforeNow: [['addWeeks', 1]] }],\n ['addWeeks', 1],\n ['makeInterval', ['addDays', 1]],\n ]\n }\n if (this.find('Last')) {\n return [\n ['setDay', dayOfWeek],\n ['startOfDay'],\n ['addDays', 1],\n ['if', { afterNow: [['addWeeks', -1]] }],\n ['addDays', -1],\n ['makeInterval', ['addDays', 1]],\n ]\n }\n if (this.find('BeforeLast')) {\n return [\n ['setDay', dayOfWeek],\n ['startOfDay'],\n ['addDays', 1],\n ['if', { afterNow: [['addWeeks', -1]] }],\n ['addDays', -1],\n ['addWeeks', -1],\n ['makeInterval', ['addDays', 1]],\n ]\n }\n return []\n }\n}\n\nexport const RelativeDayOfWeek = named(\n 'RelativeDayOfWeek',\n oneOf(\n group(named('Last', 'last'), space, DayOfWeek),\n group(named('Next', 'next'), space, DayOfWeek),\n group(DayOfWeek, space, named('BeforeLast', /before\\s+last/i)),\n group(DayOfWeek, space, named('AfterNext', /after\\s+next/i))\n )\n).parseAs(RelativeDayOfWeekNode)\n\nconst SpecificDay = oneOf(RelativeDay, DayDate, RelativeDayOfWeek, DayOfWeek)\nconst RangeEndSpecificDay = oneOf(\n RangeEndRelativeDay,\n DayDate,\n RelativeDayOfWeek,\n DayOfWeek\n)\n\nexport abstract class RangeEndRelativeIntervalNode extends RelativeIntervalNode {\n dateFns(): DateFn[] {\n return [['now'], ...super.dateFns()]\n }\n}\n\nexport class RangeEndRelativeSecondNode extends RangeEndRelativeIntervalNode {\n get intervalName(): 'Second' {\n return 'Second'\n }\n}\nexport class RangeEndRelativeMinuteNode extends RangeEndRelativeIntervalNode {\n get intervalName(): 'Minute' {\n return 'Minute'\n }\n}\nexport class RangeEndRelativeHourNode extends RangeEndRelativeIntervalNode {\n get intervalName(): 'Hour' {\n return 'Hour'\n }\n}\nexport class RangeEndRelativeWeekNode extends RangeEndRelativeIntervalNode {\n get intervalName(): 'Week' {\n return 'Week'\n }\n}\nexport class RangeEndRelativeMonthNode extends RangeEndRelativeIntervalNode {\n get intervalName(): 'Month' {\n return 'Month'\n }\n}\nexport class RangeEndRelativeYearNode extends RangeEndRelativeIntervalNode {\n get intervalName(): 'Year' {\n return 'Year'\n }\n}\n\nconst RangeEndRelativeIntervalNodes = {\n Second: RangeEndRelativeSecondNode,\n Minute: RangeEndRelativeMinuteNode,\n Hour: RangeEndRelativeHourNode,\n Week: RangeEndRelativeWeekNode,\n Month: RangeEndRelativeMonthNode,\n Year: RangeEndRelativeYearNode,\n}\n\nexport const RangeEndRelativeInterval = (\n intervalName: Exclude<RelativeIntervalType, 'Day'>\n) =>\n named(\n `RangeEndRelative${intervalName}`,\n RelativeIntervalBase(intervalName)\n ).parseAs(RangeEndRelativeIntervalNodes[intervalName])\n\nexport const RangeEndRelativeSecond = RangeEndRelativeInterval('Second')\nexport const RangeEndRelativeMinute = RangeEndRelativeInterval('Minute')\nexport const RangeEndRelativeHour = RangeEndRelativeInterval('Hour')\nexport const RangeEndRelativeWeek = RangeEndRelativeInterval('Week')\nexport const RangeEndRelativeMonth = RangeEndRelativeInterval('Month')\nexport const RangeEndRelativeYear = RangeEndRelativeInterval('Year')\n\nfunction negateDateFns(dateFns: DateFn[]): DateFn[] {\n return dateFns.map((fn) => {\n switch (fn[0]) {\n case 'addDays':\n case 'addHours':\n case 'addMilliseconds':\n case 'addMinutes':\n case 'addMonths':\n case 'addSeconds':\n case 'addWeeks':\n case 'addYears':\n return [fn[0], -fn[1]]\n }\n return fn\n })\n}\n\nclass DateTimeOffsetIntervalUnitNode extends ParseNode {\n dateFns(input: string): DateFn[] {\n switch (this.substringOf(input).toLowerCase()) {\n case 'year':\n case 'yr':\n return [['addYears', 1]]\n case 'month':\n case 'mon':\n return [['addMonths', 1]]\n case 'week':\n case 'wk':\n return [['addWeeks', 1]]\n case 'day':\n return [['addDays', 1]]\n case 'hour':\n case 'hr':\n return [['addHours', 1]]\n case 'minute':\n case 'min':\n return [['addMinutes', 1]]\n case 'second':\n case 'sec':\n return [['addSeconds', 1]]\n }\n throw new Error(`invalid input`)\n }\n}\n\nconst DateTimeOffsetIntervalUnit = named(\n 'DateTimeOffsetIntervalUnit',\n /year|yr|month|mon|week|wk|day|hour|hr|minute|min|second|sec/i\n).parseAs(DateTimeOffsetIntervalUnitNode)\n\nexport class DateTimeOffsetIntervalNode extends ParseNode {\n dateFns(input: string): DateFn[] {\n const fns =\n this.find(DateTimeIntervalNode)?.dateFns(input) ??\n this.find(DateTimeOffsetIntervalUnitNode)?.dateFns(input)\n if (!fns) throw new Error(`expected to find a DateTimeIntervalNode`)\n return this.find('Past')\n ? [...negateDateFns(fns), ['makeInterval', ['now']]]\n : [['makeInterval', ...fns]]\n }\n}\n\nexport class RangeEndDateTimeOffsetIntervalNode extends DateTimeOffsetIntervalNode {\n dateFns(input: string): DateFn[] {\n return [['now'], ...super.dateFns(input)]\n }\n}\n\nexport const DateTimeOffsetIntervalBase = group(\n oneOf(\n named('Past', /(the\\s+)?(past|last)/i),\n named('Future', /(the\\s+)?(next|coming)/i)\n ),\n space,\n oneOf(DateTimeInterval, DateTimeOffsetIntervalUnit)\n)\n\nexport const DateTimeOffsetInterval = named(\n 'DateTimeOffsetInterval',\n DateTimeOffsetIntervalBase\n).parseAs(DateTimeOffsetIntervalNode)\n\nexport const RangeEndDateTimeOffsetInterval = named(\n 'RangeEndDateTimeOffsetInterval',\n DateTimeOffsetIntervalBase\n).parseAs(RangeEndDateTimeOffsetIntervalNode)\n\nexport class DateTimeNode extends ParseNode {\n date(input: string): DateFn[] | undefined {\n return (\n this.find(DateNode) ||\n this.find(RelativeDayNode) ||\n this.find(RelativeDayOfWeekNode) ||\n this.find(DayOfWeekNode) ||\n this.find(RelativeIntervalNode) ||\n this.find(RelativeMonthNameNode) ||\n this.find(MonthNameNode) ||\n this.find(DateTimeOffsetNode) ||\n this.find(DateTimeOffsetIntervalNode) ||\n this.find(NowNode)\n )?.dateFns(input)\n }\n time(input: string): DateFn[] | undefined {\n return this.find(TimeNode)?.dateFns(input)\n }\n\n dateFns(input: string): DateFn[] {\n const Time = this.time(input)\n const Date = this.date(input)\n if (Date && Time) {\n const lastIfIndex = Date.findIndex((op) => op[0] === 'if')\n return [\n ...Date.filter(\n (op, index) =>\n op[0] !== 'makeInterval' &&\n (index < lastIfIndex || !op[0].startsWith('startOf'))\n ),\n ...Time,\n ]\n }\n return Date || Time || []\n }\n}\n\nexport const DateTime = named(\n 'DateTime',\n longestOf(\n Date,\n RelativeSecond,\n RelativeMinute,\n RelativeHour,\n RelativeWeek,\n RelativeMonthName,\n MonthName,\n RelativeMonth,\n DateTimeOffsetInterval,\n group(\n oneOf(DateTimeOffset, SpecificDay),\n group(/\\s+(at\\s+)?|\\s*,\\s*|\\s+/i, AtTime).maybe()\n ),\n group(Time, group(/\\s+(on\\s+)?|\\s*,\\s*|\\s+/i, SpecificDay).maybe()),\n group(AtTime, group(/\\s+(on\\s+)?|\\s*,\\s*/i, SpecificDay)),\n Now\n )\n).parseAs(DateTimeNode)\n\nexport const RangeEndDateTime = named(\n 'RangeEndDateTime',\n longestOf(\n Date,\n RangeEndRelativeSecond,\n RangeEndRelativeMinute,\n RangeEndRelativeHour,\n RangeEndRelativeWeek,\n RangeEndRelativeMonth,\n RangeEndDateTimeOffsetInterval,\n group(\n oneOf(RangeEndDateTimeOffset, RangeEndSpecificDay),\n group(space, group('at', space).maybe(), AtTime).maybe()\n ),\n group(\n Time,\n group(space, group('on', space).maybe(), RangeEndSpecificDay).maybe()\n ),\n group(AtTime, group(space, group('on', space), RangeEndSpecificDay)),\n Now\n )\n).parseAs(DateTimeNode)\n\nexport class RangeNode extends ParseNode {\n dateFns(input: string): DateFn[] {\n const RangeStart = this.find('RangeStart')?.find(DateTimeNode)\n if (!RangeStart) throw new Error('unexpected')\n let start = RangeStart.dateFns(input)\n const RangeEnd = this.find('RangeEnd')?.find(DateTimeNode)\n if (!RangeEnd) throw new Error('unexpected')\n let end = RangeEnd.dateFns(input)\n\n if (\n !start.some((f) => f[0] === 'setYear' || f[0] === 'addYears') &&\n end.some((f) => f[0] === 'setYear' || f[0] === 'addYears')\n ) {\n start = [\n ...end.filter(\n (f) =>\n f[0] === 'setYear' || f[0] === 'addYears' || f[0] === 'startOfYear'\n ),\n ...start.filter((f) => f[0] !== 'closestToNow'),\n ]\n end = end.filter(\n (f) =>\n f[0] !== 'setYear' && f[0] !== 'addYears' && f[0] !== 'startOfYear'\n )\n }\n\n const through = this.find('Through') != null\n\n const endFns = end.flatMap((fn) =>\n fn[0] === 'makeInterval' ? (fn.slice(1) as DateFn[]) : [fn]\n )\n if (\n !through &&\n end.find((fn) => fn[0] === 'makeInterval') &&\n endFns[endFns.length - 1]?.[0]?.startsWith('add')\n ) {\n endFns.pop()\n }\n\n return [\n ...start.filter((fn) => fn[0] !== 'makeInterval'),\n ['makeInterval', ...endFns],\n ]\n }\n}\n\nexport const Range = named(\n 'Range',\n group(\n group('from', space).maybe(),\n named('RangeStart', DateTime),\n oneOf(\n group(space, oneOf('to', named('Through', 'through'), 'until'), space),\n /\\s*-\\s*/\n ),\n named('RangeEnd', RangeEndDateTime)\n )\n).parseAs(RangeNode)\n\nexport class RootNode extends ParseRootNode {\n dateFns(input: string): DateFn[] {\n return (\n (this.find(RangeNode) || this.find(DateTimeNode))?.dateFns(input) || []\n )\n }\n}\n\nexport const Root = group(\n space.maybe(),\n oneOf(Range, DateTime),\n space.maybe()\n).parseAs(RootNode)\n\nexport function parse(input: string) {\n return base.parse(input, { grammar: Root })\n}\n\nexport function tellMeWhen(when: string, options?: { now?: Date }) {\n return base.tellMeWhen(when, { ...options, grammar: Root })\n}\n"],"mappings":"AACA,SAASA,WAAW;AACpB,SAASC,SAAS;AAClB,SAASC,aAAa;AACtB,OAAO,KAAKC,IAAI;AAEhB,MAAM;EAAEC,KAAK;EAAEC,KAAK;EAAEC,KAAK;EAAEC,KAAK;EAAEC,SAAS;EAAEC;AAAkB,CAAC,GAAGT,WAAW;AAEhF,OAAO,MAAMU,KAAK,GAAGN,KAAK,CAAC,KAAK,CAAC;AAEjC,OAAO,MAAMO,YAAY,SAASV,SAAS,CAAC;EAC1CW,WAAWA,CAAQC,OAAkB,EAAE;IACrC,KAAK,CAAC,UAAU,EAAEA,OAAO,CAACC,IAAI,EAAED,OAAO,CAACE,EAAE,CAAC;IAAA,KAD1BF,OAAkB,GAAlBA,OAAkB;EAErC;EAEAG,IAAIA,CAACC,KAAa,EAAE;IAClB,OAAOC,QAAQ,CAAC,IAAI,CAACC,WAAW,CAACF,KAAK,CAAC,CAAC;EAC1C;EAEAG,OAAOA,CAACH,KAAa,EAAY;IAC/B,OAAO,CAAC,CAAC,SAAS,EAAE,IAAI,CAACD,IAAI,CAACC,KAAK,CAAC,CAAC,CAAC;EACxC;AACF;AACA,MAAMI,QAAQ,GAAGjB,KAAK,CAAC,OAAO,CAAC,CAACkB,OAAO,CAACX,YAAY,CAAC;AAErD,OAAO,MAAMY,gBAAgB,SAAStB,SAAS,CAAC;EAC9CW,WAAWA,CAAQC,OAAkB,EAAE;IACrC,KAAK,CAAC,cAAc,EAAEA,OAAO,CAACC,IAAI,EAAED,OAAO,CAACE,EAAE,CAAC;IAAA,KAD9BF,OAAkB,GAAlBA,OAAkB;EAErC;EAEAG,IAAIA,CAACC,KAAa,EAAE;IAClB,MAAMO,MAAM,GAAGN,QAAQ,CAAC,IAAI,CAACC,WAAW,CAACF,KAAK,CAAC,CAACQ,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAClE,OAAOD,MAAM,IAAI,EAAE,GAAG,IAAI,GAAGA,MAAM,GAAG,IAAI,GAAGA,MAAM;EACrD;EAEAJ,OAAOA,CAACH,KAAa,EAAY;IAC/B,OAAO,CAAC,CAAC,SAAS,EAAE,IAAI,CAACD,IAAI,CAACC,KAAK,CAAC,CAAC,CAAC;EACxC;AACF;AACA,MAAMS,YAAY,GAAGtB,KAAK,CAAC,QAAQ,CAAC,CAACkB,OAAO,CAACC,gBAAgB,CAAC;AAE9D,MAAMI,OAAO,GAAGN,QAAQ,CAACO,EAAE,CAACF,YAAY,CAAC;AAEzC,MAAMG,cAAc,GAAGtB,KAAK,CAC1Bc,QAAQ,EACRjB,KAAK,CAAC,OAAO,CAAC,CAACkB,OAAO,CAACC,gBAAgB,CAAC,EACxClB,KAAK,CACHD,KAAK,CAAC,MAAM,CAAC,CAACkB,OAAO,CAACC,gBAAgB,CAAC,EACvCd,iBAAiB,CAAC,kBAAkB,CACtC,CACF,CAAC;AAED,OAAO,MAAMqB,YAAY,SAAS7B,SAAS,CAAC;EAC1CW,WAAWA,CAAQC,OAAkB,EAAE;IACrC,KAAK,CAAC,UAAU,EAAEA,OAAO,CAACC,IAAI,EAAED,OAAO,CAACE,EAAE,CAAC;IAAA,KAD1BF,OAAkB,GAAlBA,OAAkB;EAErC;EAEAkB,KAAKA,CAACd,KAAa,EAAE;IACnB,OAAOC,QAAQ,CAAC,IAAI,CAACC,WAAW,CAACF,KAAK,CAAC,CAAC,GAAG,CAAC;EAC9C;EAEAG,OAAOA,CAACH,KAAa,EAAY;IAC/B,OAAO,CACL,CAAC,UAAU,EAAE,IAAI,CAACc,KAAK,CAACd,KAAK,CAAC,CAAC,EAC/B,CACE,cAAc,EACd,CAAC,CAAC,IAAI,EAAE;MAAEe,QAAQ,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAAE,CAAC,CAAC,CAAC,EAC1C,CAAC,CAAC,IAAI,EAAE;MAAEC,SAAS,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAAE,CAAC,CAAC,CAAC,CAC3C,EACD,CAAC,cAAc,CAAC,EAChB,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CACnC;EACH;AACF;AACA,MAAMC,QAAQ,GAAG9B,KAAK,CAAC,gBAAgB,CAAC,CAACkB,OAAO,CAACQ,YAAY,CAAC;AAE9D,OAAO,MAAMK,aAAa,SAASlC,SAAS,CAAC;EAC3C,OAAOmC,MAAM,GAAG;IACdC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,CAAC;IACNC,GAAG,EAAE,EAAE;IACPC,GAAG,EAAE;EACP,CAAC;EAEDjB,KAAKA,CAACd,KAAa,EAAE;IACnB,OAAOkB,aAAa,CAACC,MAAM,CACzBnB,KAAK,CACFgC,SAAS,CAAC,IAAI,CAACnC,IAAI,EAAE,IAAI,CAACA,IAAI,GAAG,CAAC,CAAC,CACnCoC,WAAW,CAAC,CAAC,CACjB;EACH;EAEA9B,OAAOA,CAACH,KAAa,EAAY;IAC/B,MAAMc,KAAK,GAAG,IAAI,CAACA,KAAK,CAACd,KAAK,CAAC;IAC/B,OAAO,CACL,CAAC,UAAU,EAAEc,KAAK,CAAC,EACnB,CACE,cAAc,EACd,CAAC,CAAC,IAAI,EAAE;MAAEC,QAAQ,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAAE,CAAC,CAAC,CAAC,EAC1C,CAAC,CAAC,IAAI,EAAE;MAAEC,SAAS,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAAE,CAAC,CAAC,CAAC,CAC3C,EACD,CAAC,cAAc,CAAC,EAChB,CAAC,cAAc,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CACnC;EACH;AACF;AACA,MAAMkB,aAAa,GAAG7C,KAAK,CACzB,eAAe,EACf,mGACF,CAAC,CAACgB,OAAO,CAACa,aAAa,CAAC;AAExB,MAAMiB,eAAe,GAAG9C,KAAK,CAC3B,iBAAiB,EACjB,+DACF,CAAC,CAACgB,OAAO,CAACa,aAAa,CAAC;AAExB,MAAMkB,SAAS,GAAGF,aAAa,CAACvB,EAAE,CAACvB,KAAK,CAAC+C,eAAe,EAAE/C,KAAK,CAAC,GAAG,CAAC,CAACiD,KAAK,CAAC,CAAC,CAAC,CAAC;AAE9E,OAAO,MAAMC,qBAAqB,SAAStD,SAAS,CAAC;EACnDmB,OAAOA,CAACH,KAAa,EAAY;IAAA,IAAAuC,UAAA;IAC/B,MAAMzB,KAAK,IAAAy