UNPKG

serina

Version:

Natural Language Parser for date and time in Javascript

1 lines 84.2 kB
{"version":3,"file":"serina.modern.mjs","sources":["../src/lib/string/stringUtil.ts","../src/filters/filter.ts","../src/utils/wrapInBracket.ts","../src/filters/weekday/weekday.constants.ts","../src/lib/date/helper.ts","../src/lib/date/dayLite.ts","../src/filters/weekday/weekday.helpers.ts","../src/filters/weekday/weekday.ts","../src/filters/day/day.constants.ts","../src/filters/day/day.ts","../src/filters/day/day.helpers.ts","../src/filters/month/month.constants.ts","../src/filters/month/month.helpers.ts","../src/filters/month/month.ts","../src/filters/year/year.constants.ts","../src/filters/year/year.ts","../src/filters/year/year.helpers.ts","../src/filters/time/time.constants.ts","../src/filters/time/time.helpers.ts","../src/filters/time/time.ts","../src/filters/dates/dates.constants.ts","../src/filters/dates/dates.helpers.ts","../src/filters/dates/dates.ts","../src/filters/partialDates/partialDates.constants.ts","../src/filters/partialDates/partialDates.helpers.ts","../src/filters/relativeDates/relativeDates.constants.ts","../src/filters/relativeDates/relativeDates.helpers.ts","../src/utils/findMatchingKey.ts","../src/filters/dateAndTime/dateAndTime.constants.ts","../src/filters/dateAndTime/dateAndTime.ts","../src/filters/dateAndTime/dateAndTime.helpers.ts","../src/filters/weekdayAndTime/weekdayAndTime.constants.ts","../src/filters/weekdayAndTime/weekdayAndTime.ts","../src/filters/weekdayAndTime/weekdayAndTime.helpers.ts","../src/filters/relativeTime/relativeTime.constants.ts","../src/filters/relativeTime/relativeTime.ts","../src/filters/relativeTime/relativeTime.helpers.ts","../src/filters/relativeDates/relativeDates.ts","../src/filters/timeKeywords/timeKeywords.constants.ts","../src/filters/timeKeywords/timeKeywords.ts","../src/filters/timeKeywords/timeKeywords.helpers.ts","../src/filters/partialDates/partialDates.ts","../src/serina.ts"],"sourcesContent":["import { ParsedMatchSchema } from 'serina.schema';\n\nexport function matchPattern(haystack: string, pattern: string, wordBoundary = true): string[] {\n const updatedPattern = wordBoundary ? `\\\\b${pattern}\\\\b` : pattern;\n const regex = new RegExp(updatedPattern, 'ig');\n return haystack.toLowerCase().match(regex);\n}\n\nexport function contains(haystack: string, pattern: string, wordBoundary = true) {\n const updatedPattern = wordBoundary ? `\\\\b${pattern}\\\\b` : pattern;\n const regex = new RegExp(updatedPattern, 'i');\n return regex.test(haystack.toLowerCase());\n}\n\nexport function trimWhiteSpaces(text: string): string {\n const trimmedText = text.replace(/ {2}/g, ' ');\n return trimmedText.trim();\n}\n\nexport function parseMatches(text: string, pattern: string, dateTimeObj: Date): ParsedMatchSchema {\n const regex = new RegExp(pattern, 'ig');\n const textRemain = text.replace(regex, '');\n\n // get the original capitalisation\n const [matched] = text.match(regex);\n\n return {\n text: trimWhiteSpaces(textRemain),\n dateTime: dateTimeObj,\n matched: trimWhiteSpaces(matched),\n };\n}\n\nexport function remove(text: string, pattern: string, wordBoundary = true) {\n const updatedPattern = wordBoundary ? `\\\\b${pattern}\\\\b` : pattern;\n const regex = new RegExp(updatedPattern, 'ig');\n const replacedText = text.replace(regex, '');\n return trimWhiteSpaces(replacedText);\n}\n","import { matchPattern, parseMatches } from 'lib/string/stringUtil';\nimport { ParsedMatchSchema } from 'serina.schema';\n\nexport default class Filter {\n _pattern: string;\n _wordBoundary: boolean;\n\n constructor(pattern = '', wordBoundary?: boolean) {\n this._pattern = pattern;\n this._wordBoundary = wordBoundary;\n }\n\n parseText(text: string): ParsedMatchSchema[] {\n const matches = matchPattern(text, this._pattern, this._wordBoundary);\n\n if (!matches) return null;\n\n return matches.map(match => {\n const dateObj = this.parseStringToDateObj(match);\n return parseMatches(text, match, dateObj);\n });\n }\n\n parseStringToDateObj(match: string): Date {\n return isNaN(Date.parse(match)) ? new Date('2000-01-01T00:00:00Z') : new Date(match);\n }\n}\n","export function wrapInBracket(text: string): string {\n return `(${text})`;\n}\n","import { wrapInBracket } from 'utils/wrapInBracket';\n\nconst MONDAY = '(mon)(day)?';\nconst TUESDAY = '(tue(s)?)(day)?';\nconst WEDNESDAY = '(wed|wedn(es)?)(day)?';\nconst THURSDAY = '(thu(r)?(s)?)(day)?';\nconst FRIDAY = '(fri)(day)?';\nconst SATURDAY = '(sat(ur)?)(day)?';\nconst SUNDAY = '(sun)(day)?';\n\nconst ANY = wrapInBracket([MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY].join('|'));\nconst FUTURE_WORDS = 'for|next|this|current|on';\nconst PAST_WORDS = 'last|prev(ious)?';\n\nconst WEEKDAY = {\n WITH_FUTURE_PAST_WORDS: `((\\\\b(${FUTURE_WORDS}|${PAST_WORDS})\\\\b)( ))?${ANY}`,\n ANY,\n SINGLE: {\n MONDAY,\n TUESDAY,\n WEDNESDAY,\n THURSDAY,\n FRIDAY,\n SATURDAY,\n SUNDAY,\n },\n PAST_WORDS,\n};\n\nexport default WEEKDAY;\n","import { DayLiteUnits } from './types';\n\nexport function getStartOfWeek(weekday: number, day: number): number {\n if (weekday === 0) {\n // if weekday is Sunday\n return day - 6;\n }\n\n return day - weekday + 1; // plus 1 here because first Monday is first day of the week\n}\n\nexport function getDaysInMonth(year: number, month: number): number {\n return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();\n}\n\nexport function nextMonths(date: Date, value: number): Date {\n const day = date.getUTCDate();\n const month = date.getUTCMonth();\n const year = date.getUTCFullYear();\n\n let count = value;\n // loop to skip any months without the day\n // e.g. given date is 31st March, since Apr does not have 31st, so skip to May\n while (day > getDaysInMonth(year, month + count)) {\n if (count > 100) throw 'Possible infinite loop within nextMonths()';\n count++;\n }\n\n const newDate = date;\n newDate.setUTCMonth(month + count);\n return newDate;\n}\n\nexport function nextYears(date: Date, value: number): Date {\n const day = date.getUTCDate();\n const month = date.getUTCMonth();\n const year = date.getUTCFullYear();\n\n let count = value;\n // loop to skip any year without the day or has diff month\n // e.g.given date is 2000 Feb 29th, since 2001 Feb does not have 29th, so skip to 2004\n while (\n day > getDaysInMonth(year + count, month) ||\n new Date(Date.UTC(year + count, month, day)).getUTCMonth() != month\n ) {\n if (count > 100) throw 'Possible infinite loop within nextYears()';\n count++;\n }\n\n const newDate = date;\n newDate.setUTCFullYear(year + count);\n return newDate;\n}\n\nexport function prevMonths(date: Date, value: number): Date {\n const day = date.getUTCDate();\n const month = date.getUTCMonth();\n const year = date.getUTCFullYear();\n\n let count = value;\n // loop to skip any months without the day\n // e.g. given date is May 31st, since Apr does not have 31st, so skip to Mar\n\n while (day > getDaysInMonth(year, month - count)) {\n if (count > 100) throw 'Possible infinite loop within prevMonths()';\n count++;\n }\n\n const newDate = date;\n newDate.setUTCMonth(month - count);\n return newDate;\n}\n\nexport function prevYears(date: Date, value: number): Date {\n const day = date.getUTCDate();\n const month = date.getUTCMonth();\n const year = date.getUTCFullYear();\n\n let count = value;\n // loop to skip any year without the day\n // e.g. given date is 2004 Feb 29th, since 2001 Feb does not have 29th, so skip to 2000\n while (\n day > getDaysInMonth(year - count, month) ||\n new Date(Date.UTC(year - count, month, 1)).getUTCMonth() != month\n ) {\n if (count > 100) throw 'Possible infinite loop within prevYears()';\n count++;\n }\n\n const newDate = date;\n newDate.setUTCFullYear(year - count);\n return newDate;\n}\n\nexport function orderUnits(units: DayLiteUnits[]): DayLiteUnits[] {\n const order: DayLiteUnits[] = [\n 'millisecond',\n 'second',\n 'minute',\n 'hour',\n 'day',\n 'week',\n 'weekday',\n 'month',\n 'year',\n ];\n const newList = [];\n\n order.forEach(unit => {\n if (units.includes(unit)) newList.push(unit);\n });\n\n return newList;\n}\n","import { getDaysInMonth, getStartOfWeek, nextMonths, orderUnits } from './helper';\nimport { DayLiteUnits } from './types';\n\nclass DayLite {\n private _dateTime: Date;\n\n constructor(date?: Date) {\n this._dateTime = date ? new Date(date.getTime()) : new Date();\n }\n\n get millisecond() {\n return this._dateTime.getUTCMilliseconds();\n }\n\n private set millisecond(value: number) {\n this._dateTime.setUTCMilliseconds(value);\n }\n\n get second() {\n return this._dateTime.getUTCSeconds();\n }\n\n private set second(value: number) {\n this._dateTime.setUTCSeconds(value);\n }\n\n get minute() {\n return this._dateTime.getUTCMinutes();\n }\n\n private set minute(value: number) {\n this._dateTime.setUTCMinutes(value);\n }\n\n get hour() {\n return this._dateTime.getUTCHours();\n }\n\n private set hour(value: number) {\n this._dateTime.setUTCHours(value);\n }\n\n get day() {\n return this._dateTime.getUTCDate();\n }\n\n private set day(value: number) {\n this._dateTime.setUTCDate(value);\n }\n\n get weekday() {\n return this._dateTime.getUTCDay();\n }\n\n private set weekday(value: number) {\n const diff = this.weekday - value;\n\n this.set({\n day: this.day + diff,\n });\n }\n\n get weekdayName() {\n return this._dateTime.toLocaleString('default', { weekday: 'long' });\n }\n\n get month() {\n return this._dateTime.getUTCMonth() + 1;\n }\n\n private set month(value: number) {\n this._dateTime.setUTCMonth(value - 1);\n }\n\n get nativeMonth() {\n return this._dateTime.getUTCMonth();\n }\n\n get monthName() {\n return this._dateTime.toLocaleString('default', { month: 'long' });\n }\n\n get year() {\n return this._dateTime.getUTCFullYear();\n }\n\n private set year(value: number) {\n this._dateTime.setUTCFullYear(value);\n }\n\n get leapYear() {\n return new Date(Date.UTC(this.year, 1, 29)).getUTCDate() === 29;\n }\n\n get daysInMonth() {\n return getDaysInMonth(this.year, this.nativeMonth);\n }\n\n toDate() {\n return this._dateTime;\n }\n\n toISOString() {\n return this._dateTime.toISOString();\n }\n\n toString() {\n return this.toISOString();\n }\n\n now() {\n return this._dateTime.valueOf();\n }\n\n set(changes: Partial<Record<DayLiteUnits, number>>) {\n const orderedKeys = orderUnits(Object.keys(changes) as DayLiteUnits[]);\n\n orderedKeys.forEach(key => {\n const value = changes[key];\n switch (key) {\n case 'second':\n this.second = value;\n break;\n case 'minute':\n this.minute = value;\n break;\n case 'hour':\n this.hour = value;\n break;\n case 'day':\n if (value < 1) {\n this.day = 1;\n } else if (value > this.daysInMonth) {\n this.day = this.daysInMonth;\n } else {\n this.day = value;\n }\n break;\n // case 'week':\n // this.day = value;\n // break;\n case 'weekday':\n this.day += value - this.weekday;\n break;\n case 'month':\n if (value < 1) {\n this.month = 1;\n } else if (value > 12) {\n this.month = 12;\n } else {\n this.month = value;\n }\n break;\n case 'year':\n this.year = value;\n break;\n default:\n throw 'Cannot perform .set() operation with unknown unit';\n }\n });\n\n return this;\n }\n\n plus(value: number, unit: DayLiteUnits) {\n switch (unit) {\n case 'millisecond':\n this.millisecond += value;\n break;\n case 'second':\n this.second += value;\n break;\n case 'minute':\n this.minute += value;\n break;\n case 'hour':\n this.hour += value;\n break;\n case 'day':\n this.day += value;\n break;\n case 'week':\n this.day += value * 7;\n break;\n case 'month':\n this.month += value;\n break;\n case 'year':\n this.year += value;\n break;\n default:\n throw 'Cannot perform .plus() operation with unknown unit';\n }\n\n return this;\n }\n\n minus(value: number, unit: DayLiteUnits) {\n switch (unit) {\n case 'millisecond':\n this.millisecond -= value;\n break;\n case 'second':\n this.second -= value;\n break;\n case 'minute':\n this.minute -= value;\n break;\n case 'hour':\n this.hour -= value;\n break;\n case 'day':\n this.day -= value;\n break;\n case 'week':\n this.day -= value * 7;\n break;\n case 'month':\n this.month -= value;\n break;\n case 'year':\n this.year -= value;\n break;\n default:\n throw 'Cannot perform .minus() operation with unknown unit';\n }\n\n return this;\n }\n\n next(value: number, unit: DayLiteUnits) {\n switch (unit) {\n case 'millisecond':\n this.millisecond += value;\n break;\n case 'second':\n this.second += value;\n break;\n case 'minute':\n this.minute += value;\n break;\n case 'hour':\n this.hour += value;\n break;\n case 'day':\n this.day += value;\n break;\n case 'week':\n this.day += value * 7;\n break;\n case 'month':\n this._dateTime = nextMonths(this._dateTime, value);\n break;\n case 'year':\n this.year += value;\n break;\n default:\n throw 'Cannot perform .next() operation with unknown unit';\n }\n\n return this;\n }\n\n prev(value: number, unit: DayLiteUnits) {\n return this.previous(value, unit);\n }\n\n previous(value: number, unit: DayLiteUnits) {\n switch (unit) {\n case 'millisecond':\n this.millisecond -= value;\n break;\n case 'second':\n this.second -= value;\n break;\n case 'minute':\n this.minute -= value;\n break;\n case 'hour':\n this.hour -= value;\n break;\n case 'day':\n this.day -= value;\n break;\n case 'week':\n this.day -= value * 7;\n break;\n case 'month':\n this.month -= value;\n break;\n case 'year':\n this.year -= value;\n break;\n default:\n throw 'Cannot perform .prev() or .previous() operation with unknown unit';\n }\n\n return this;\n }\n\n start(unit: DayLiteUnits) {\n return this.startOf(unit);\n }\n\n startOf(unit: DayLiteUnits) {\n switch (unit) {\n case 'millisecond':\n this.millisecond = 0;\n break;\n case 'second':\n this.millisecond = 0;\n break;\n case 'minute':\n this.millisecond = 0;\n this.second = 0;\n break;\n case 'hour':\n this.millisecond = 0;\n this.second = 0;\n this.minute = 0;\n break;\n case 'day':\n this.millisecond = 0;\n this.second = 0;\n this.minute = 0;\n this.hour = 0;\n break;\n case 'week':\n this.millisecond = 0;\n this.second = 0;\n this.minute = 0;\n this.hour = 0;\n this.day = getStartOfWeek(this.weekday, this.day);\n break;\n case 'month':\n this.millisecond = 0;\n this.second = 0;\n this.minute = 0;\n this.hour = 0;\n this.day = 1;\n break;\n case 'year':\n this.millisecond = 0;\n this.second = 0;\n this.minute = 0;\n this.hour = 0;\n this.day = 1;\n this.month = 1;\n break;\n default:\n throw 'Cannot perform .start() or .startOf() operation with unknown unit';\n }\n\n return this;\n }\n\n end(unit: DayLiteUnits) {\n return this.endOf(unit);\n }\n\n endOf(unit: DayLiteUnits) {\n switch (unit) {\n case 'second':\n this.millisecond = 999;\n break;\n case 'minute':\n this.millisecond = 999;\n this.second = 59;\n break;\n case 'hour':\n this.millisecond = 999;\n this.second = 59;\n this.minute = 59;\n break;\n case 'day':\n this.millisecond = 999;\n this.second = 59;\n this.minute = 59;\n this.hour = 23;\n break;\n case 'month':\n this.millisecond = 999;\n this.second = 59;\n this.minute = 59;\n this.hour = 23;\n this.day = new Date(Date.UTC(this.year, this.month, 0)).getUTCDate();\n break;\n case 'year':\n this.millisecond = 999;\n this.second = 59;\n this.minute = 59;\n this.hour = 23;\n this.day = new Date(Date.UTC(this.year, this.month, 0)).getUTCDate();\n this.month = 12;\n break;\n default:\n throw 'Cannot perform .end() or .endOf() operation with unknown unit';\n }\n\n return this;\n }\n}\n\nexport function dayLite(date?: Date) {\n return new DayLite(date);\n}\n","import WEEKDAY from 'filters/weekday/weekday.constants';\nimport { contains, matchPattern } from 'lib/string/stringUtil';\nimport { dayLite } from 'lib/date/dayLite';\n\nexport function weekdayStringToNumber(weekdayString: string, pastWeekday: boolean): number {\n let weekday = null;\n const todayInWeekday = dayLite().weekday;\n\n // Logic here assumes Monday is the first day of the week\n // TODO: Make this logic more robust\n Object.keys(WEEKDAY.SINGLE).forEach((key, index) => {\n const weekdayPattern = WEEKDAY.SINGLE[key];\n if (contains(weekdayString, weekdayPattern)) {\n weekday = index + 1;\n }\n });\n if (!weekday) return null;\n if (weekday <= todayInWeekday) weekday += 7;\n if (pastWeekday) weekday -= 7;\n\n return weekday;\n}\n\nexport function weekdayStringToDateObj(matchingText: string) {\n const [weekdayString] = matchPattern(matchingText, WEEKDAY.ANY);\n const pastWeekday: boolean = contains(matchingText, WEEKDAY.PAST_WORDS);\n const weekday = weekdayStringToNumber(weekdayString, pastWeekday);\n\n return dayLite().set({ weekday }).startOf('day').toDate();\n}\n","import Filter from 'filters/filter';\nimport WEEKDAY from './weekday.constants';\nimport { weekdayStringToDateObj } from './weekday.helpers';\n\nexport default class Weekday extends Filter {\n constructor() {\n // When parsing day of the week, check for relative words & week day e.g. next friday\n super(WEEKDAY.WITH_FUTURE_PAST_WORDS);\n }\n\n parseStringToDateObj(match: string): Date {\n return weekdayStringToDateObj(match);\n }\n}\n","const NUM_DAY = '((3[0-1])|([1-2][0-9])|(0?[1-9]))';\nconst ORDINAL_INDICATORS = '(st|nd|rd|th)';\nconst FILLER_WORDS = 'on (the )?';\nconst WITH_ORDINAL = `${NUM_DAY}${ORDINAL_INDICATORS}`;\n\nconst DAY = {\n ANY: `${NUM_DAY}${ORDINAL_INDICATORS}?`,\n WITH_ORDINAL: WITH_ORDINAL,\n NUMBERS: NUM_DAY,\n FILLER_WORDS,\n WITH_FILLER_WORDS_AND_ORDINAL: `(${FILLER_WORDS})?${WITH_ORDINAL}`,\n};\n\nexport default DAY;\n","import DAY from './day.constants';\nimport { dayStringToDateObj } from './day.helpers';\nimport Filter from 'filters/filter';\n\nexport default class Day extends Filter {\n constructor() {\n super(DAY.WITH_FILLER_WORDS_AND_ORDINAL);\n }\n\n parseStringToDateObj(match: string): Date {\n return dayStringToDateObj(match);\n }\n}\n","import { dayLite } from 'lib/date/dayLite';\nimport { contains, matchPattern } from 'lib/string/stringUtil';\nimport DAY from './day.constants';\n\nexport function dayStringToDateObj(matchingText: string): Date {\n const today = dayLite();\n let day: number = null;\n let month: number = today.month;\n\n if (contains(matchingText, DAY.ANY)) {\n const [matchedDay] = matchPattern(matchingText, DAY.ANY);\n day = parseInt(matchedDay, 10);\n\n // if day is in past then try future month\n if (day < today.day) month += 1;\n }\n\n if (!day) return null;\n\n return dayLite().set({ day, month }).start('day').toDate();\n}\n","import { wrapInBracket } from 'utils/wrapInBracket';\n\nconst JANUARY = 'jan(uary)?';\nconst FEBRUARY = 'feb(ruary)?';\nconst MARCH = 'mar(ch)?';\nconst APRIL = 'apr(il)?';\nconst MAY = 'may';\nconst JUNE = 'jun(e)?';\nconst JULY = 'jul(y)?';\nconst AUGUST = 'aug(ust)?';\nconst SEPTEMBER = 'sep(tember)?';\nconst OCTOBER = 'oct(ober)?';\nconst NOVEMBER = 'nov(ember)?';\nconst DECEMBER = 'dec(ember)?';\nconst FUTURE_WORDS = '(for|next|this|current|in)';\nconst PAST_WORDS = '(last|prev(ious)?)';\nconst ANY = wrapInBracket(\n [JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER].join('|')\n);\n\nconst MONTH = {\n WITH_FUTURE_PAST_WORDS: `((${FUTURE_WORDS}|${PAST_WORDS}) )?${ANY}`,\n ANY,\n SINGLE: {\n JANUARY,\n FEBRUARY,\n MARCH,\n APRIL,\n MAY,\n JUNE,\n JULY,\n AUGUST,\n SEPTEMBER,\n OCTOBER,\n NOVEMBER,\n DECEMBER,\n },\n NUMBERS: '(1[0-2]|0?[1-9])',\n PAST_WORDS,\n};\n\nexport default MONTH;\n","import { dayLite } from 'lib/date/dayLite';\nimport { contains } from 'lib/string/stringUtil';\nimport MONTH from './month.constants';\n\nexport function monthStringToNumber(matchingText: string): number {\n let month = null;\n\n Object.keys(MONTH.SINGLE).forEach((key, index) => {\n const monthPattern = MONTH.SINGLE[key];\n if (contains(matchingText, monthPattern)) {\n month = index + 1;\n }\n });\n\n return month;\n}\n\nexport function monthStringToDateObj(matchingText: string): Date {\n const month = monthStringToNumber(matchingText);\n\n if (month === null) return null;\n\n let year = dayLite().year;\n\n if (month < dayLite().month) year += 1;\n if (contains(matchingText, `${MONTH.PAST_WORDS} ${MONTH.ANY}`)) year -= 1;\n\n return dayLite().set({ month, year }).startOf('month').start('day').toDate();\n}\n","import Filter from 'filters/filter';\nimport MONTH from './month.constants';\nimport { monthStringToDateObj } from './month.helpers';\n\nexport default class Month extends Filter {\n constructor() {\n super(MONTH.WITH_FUTURE_PAST_WORDS);\n }\n\n parseStringToDateObj(match: string): Date {\n return monthStringToDateObj(match);\n }\n}\n","const ANY = '(\\\\b[0-9]{4}\\\\b)';\nconst FILLER_WORDS = '(in( )((the( ))?year( ))?)';\n\nconst YEAR = {\n ANY,\n FILLER_WORDS,\n WITH_FILLER_WORDS: `${FILLER_WORDS}?${ANY}`,\n};\n\nexport default YEAR;\n","import Filter from 'filters/filter';\nimport YEAR from './year.constants';\nimport { yearStringToDate } from './year.helpers';\n\n// parsing year between 1000 - 9999\nexport default class Year extends Filter {\n constructor() {\n super(YEAR.WITH_FILLER_WORDS);\n }\n\n parseStringToDateObj(match: string): Date {\n return yearStringToDate(match);\n }\n}\n","import { dayLite } from 'lib/date/dayLite';\nimport { contains } from 'lib/string/stringUtil';\nimport { matchPattern } from 'lib/string/stringUtil';\nimport YEAR from './year.constants';\n\nexport function yearStringToDate(matchingText: string): Date {\n let year: number;\n\n if (contains(matchingText, YEAR.ANY)) {\n const [matchedDay] = matchPattern(matchingText, YEAR.ANY);\n year = parseInt(matchedDay, 10);\n }\n\n if (!year) return null;\n\n return dayLite().set({ year }).startOf('year').toDate();\n}\n","import { wrapInBracket } from 'utils/wrapInBracket';\n\nconst AM = 'am|a.m.|am.';\nconst PM = 'pm|p.m.|pm.';\nconst MERIDIEM = `(${AM}|${PM})`;\nconst FILLER_WORDS = '(at|by)';\nconst HOUR_PART = '([0-9]{1,2})';\nconst DIVIDER = '(:)';\nconst MINUTE_PART = '([0-5][0-9])';\nconst TO = '(to|until)';\nconst PAST = '(after|past)';\nconst RELATIVE_TIME_FILLER_WORDS = wrapInBracket([TO, PAST].join('|'));\nconst HALF = 'half';\nconst QUARTER = 'quarter';\nconst VERBAL_QUANTIFIERS = wrapInBracket([HALF, QUARTER].join('|'));\nconst MINUTE_IDENTIFIER = '(min|mins|minute|minutes)';\n\nconst FORMAT_JUST_HOUR = `${HOUR_PART}( )?${MERIDIEM}`; // 4am or 4 am\nconst FORMAT_HOUR_WITH_MINS = [HOUR_PART, MINUTE_PART].join(DIVIDER); // 04:30\nconst FORMAT_HOUR_WITH_MINS_AND_MERIDIEM = `${FORMAT_HOUR_WITH_MINS}( )?${MERIDIEM}`; // 04:30am or 04:30 am\nconst FORMAT_VERBAL_QUANTIFIERS = `${VERBAL_QUANTIFIERS}( )${RELATIVE_TIME_FILLER_WORDS}( )(${FORMAT_JUST_HOUR}|${HOUR_PART})`; // half past 3pm\nconst FORMAT_DIGIT_RELATIVE = `${MINUTE_PART}( )(${MINUTE_IDENTIFIER}( ))?${RELATIVE_TIME_FILLER_WORDS}( )(${FORMAT_JUST_HOUR}|${HOUR_PART})`; // 20 mins to 11\nconst FORMAT_NORMAL = wrapInBracket(\n [FORMAT_JUST_HOUR, FORMAT_HOUR_WITH_MINS_AND_MERIDIEM, FORMAT_HOUR_WITH_MINS].join('|')\n);\nconst FORMAT_RELATIVE = wrapInBracket([FORMAT_VERBAL_QUANTIFIERS, FORMAT_DIGIT_RELATIVE].join('|'));\nconst ANY = wrapInBracket([FORMAT_NORMAL, FORMAT_RELATIVE].join('|'));\n\nconst TIME = {\n WITH_FILLER_WORDS: `(${FILLER_WORDS}( ))?${ANY}`,\n ANY,\n FORMAT_NORMAL,\n FORMAT_RELATIVE,\n RELATIVE_TIME_FILLER_WORDS,\n HOUR_PART,\n MINUTE_PART,\n DIVIDER,\n MERIDIEM,\n AM,\n PM,\n TO,\n PAST,\n HALF,\n QUARTER,\n VERBAL_QUANTIFIERS,\n MINUTE_IDENTIFIER,\n FILLER_WORDS,\n};\n\nexport default TIME;\n","import { TimeObjectSchema } from 'serina.schema';\nimport TIME from 'filters/time/time.constants';\nimport { contains } from 'lib/string/stringUtil';\nimport { matchPattern } from 'lib/string/stringUtil';\nimport { dayLite } from 'lib/date/dayLite';\n\nexport function getValidMatch(text: string, pattern: string): string {\n const matched = matchPattern(text, pattern, false);\n if (!matched || matched.length === 0) return null;\n const [value] = matched;\n return value;\n}\n\nexport function convertTime(timeString: string, hour: number, minute: number): TimeObjectSchema {\n if (isNaN(hour) || isNaN(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) return null;\n if (contains(timeString, TIME.AM, false) && hour === 12) hour = 24;\n if (contains(timeString, TIME.TO) && hour > 0) hour -= 1;\n if (contains(timeString, TIME.TO)) minute = 60 - minute;\n if (contains(timeString, TIME.PM, false) && hour < 12) hour += 12;\n if (hour === 24) hour = 0;\n\n // TODO: need to move this logic some where else\n // if (hour < dayLite().hour || (hour === dayLite().hour && minute < dayLite().minute)) hour += 24;\n\n return { hour, minute };\n}\n\nexport function timeStringToDateObj(timeString: string): TimeObjectSchema {\n let hour: string;\n let minute: string;\n const isRelativeTime = contains(timeString, TIME.RELATIVE_TIME_FILLER_WORDS);\n\n if (isRelativeTime) {\n if (contains(timeString, TIME.VERBAL_QUANTIFIERS)) {\n // half or quarter\n try {\n hour = getValidMatch(timeString, `(?<=${TIME.RELATIVE_TIME_FILLER_WORDS}( ))${TIME.HOUR_PART}`);\n } catch {\n // workaround for browsers that doesn't support regex lookbehind\n const hourWithFillerWords = getValidMatch(\n timeString,\n `(${TIME.RELATIVE_TIME_FILLER_WORDS}( ))${TIME.HOUR_PART}`\n );\n hour = getValidMatch(hourWithFillerWords, TIME.HOUR_PART);\n }\n\n if (contains(timeString, TIME.HALF)) minute = '30';\n if (contains(timeString, TIME.QUARTER)) minute = '15';\n } else {\n // 20 min to 7pm\n try {\n hour = getValidMatch(\n timeString,\n `(?<=${TIME.RELATIVE_TIME_FILLER_WORDS}( ))${TIME.HOUR_PART}(?=((( )${TIME.MERIDIEM}))?)`\n );\n } catch {\n // workaround for browsers that doesn't support regex lookbehind\n const hourWithFillerWords = getValidMatch(\n timeString,\n `(${TIME.RELATIVE_TIME_FILLER_WORDS}( ))${TIME.HOUR_PART}(?=((( )${TIME.MERIDIEM}))?)`\n );\n hour = getValidMatch(hourWithFillerWords, TIME.HOUR_PART);\n }\n minute = getValidMatch(\n timeString,\n `\\\\b${TIME.MINUTE_PART}(?=(( )(${TIME.MINUTE_IDENTIFIER}( ))?${TIME.RELATIVE_TIME_FILLER_WORDS}))`\n );\n }\n } else {\n if (contains(timeString, TIME.DIVIDER)) {\n // 19:09\n hour = getValidMatch(timeString, `\\\\b${TIME.HOUR_PART}(?=${TIME.DIVIDER})`);\n try {\n minute = getValidMatch(timeString, `(?<=${TIME.DIVIDER})${TIME.MINUTE_PART}`);\n } catch {\n // workaround for browsers that doesn't support regex lookbehind\n const minWithDivider = getValidMatch(timeString, `(${TIME.DIVIDER})${TIME.MINUTE_PART}`);\n minute = getValidMatch(minWithDivider, TIME.MINUTE_PART);\n }\n } else {\n // 6pm\n hour = getValidMatch(timeString, `\\\\b${TIME.HOUR_PART}(?=(( )?${TIME.MERIDIEM}))`);\n minute = '0';\n }\n }\n\n return convertTime(timeString, parseInt(hour, 10), parseInt(minute, 10));\n}\n\nexport function timeStringToHourMinute(matchingText: string): Date {\n const timeObj = timeStringToDateObj(matchingText);\n\n if (!timeObj) return null;\n\n const { hour, minute } = timeObj;\n\n return dayLite().set({ hour, minute }).startOf('minute').toDate();\n}\n","import TIME from './time.constants';\nimport Filter from 'filters/filter';\nimport { timeStringToHourMinute } from './time.helpers';\n\nexport default class Time extends Filter {\n constructor() {\n super(TIME.WITH_FILLER_WORDS, false);\n }\n\n parseStringToDateObj(match: string): Date {\n return timeStringToHourMinute(match);\n }\n}\n","import DAY from 'filters/day/day.constants';\nimport MONTH from 'filters/month/month.constants';\nimport YEAR from 'filters/year/year.constants';\nimport { wrapInBracket } from 'utils/wrapInBracket';\n\nconst FILLER_WORDS = '((on|by) (the )?)';\nconst NUM_DIVIDER = '(/|(-))';\nconst NUM_DAY_MONTH_YEAR = wrapInBracket(\n [\n wrapInBracket([DAY.NUMBERS, MONTH.NUMBERS, YEAR.ANY].join('-')), // 30-01-2023\n wrapInBracket([DAY.NUMBERS, MONTH.NUMBERS, YEAR.ANY].join('/')), // 30/01/2023\n ].join('|')\n);\nconst NUM_MONTH_DAY_YEAR = wrapInBracket(\n [\n wrapInBracket([MONTH.NUMBERS, DAY.NUMBERS, YEAR.ANY].join('-')), // 01-30-2023\n wrapInBracket([MONTH.NUMBERS, DAY.NUMBERS, YEAR.ANY].join('/')), // 01/30/2023\n ].join('|')\n);\nconst NUM_YEAR_MONTH_DAY = wrapInBracket(\n [\n wrapInBracket([YEAR.ANY, MONTH.NUMBERS, DAY.NUMBERS].join('-')), // 2023-01-30\n wrapInBracket([YEAR.ANY, MONTH.NUMBERS, DAY.NUMBERS].join('/')), // 2023/01/30\n ].join('|')\n);\nconst TXT_DIVIDER = '((,)? )';\nconst TXT_DAY_MONTH_YEAR = wrapInBracket([DAY.ANY, MONTH.ANY, YEAR.ANY].join(TXT_DIVIDER));\nconst TXT_MONTH_DAY_YEAR = wrapInBracket([MONTH.ANY, DAY.ANY, YEAR.ANY].join(TXT_DIVIDER));\nconst ANY = [NUM_DAY_MONTH_YEAR, NUM_MONTH_DAY_YEAR, NUM_YEAR_MONTH_DAY, TXT_DAY_MONTH_YEAR, TXT_MONTH_DAY_YEAR].join(\n '|'\n);\n\nconst DATES = {\n WITH_FILLER_WORDS: `(${FILLER_WORDS})?(${ANY})`,\n ANY,\n NUM_DAY_MONTH_YEAR,\n NUM_MONTH_DAY_YEAR,\n NUM_YEAR_MONTH_DAY,\n TXT_DAY_MONTH_YEAR,\n TXT_MONTH_DAY_YEAR,\n FILLER_WORDS,\n NUM_DIVIDER,\n TXT_DIVIDER,\n};\n\nexport default DATES;\n","import { monthStringToNumber } from 'filters/month/month.helpers';\nimport { dayLite } from 'lib/date/dayLite';\nimport { contains, remove } from 'lib/string/stringUtil';\nimport { DateObjectSchema } from 'serina.schema';\nimport DATES from './dates.constants';\n\nexport function strToInt(dayStr: string, monthStr: string, yearStr: string): DateObjectSchema {\n return {\n day: parseInt(dayStr, 10),\n month: parseInt(monthStr, 10),\n year: parseInt(yearStr, 10),\n };\n}\n\nexport function dateStringToDayMonthYear(date: string): Date {\n let day: string;\n let month: string;\n let year: string;\n const numDividerRegex = new RegExp(DATES.NUM_DIVIDER, 'g');\n const txtDividerRegex = new RegExp(DATES.TXT_DIVIDER, 'gi');\n\n if (contains(date, DATES.NUM_DAY_MONTH_YEAR, false)) {\n [day, month, year] = date.replace(numDividerRegex, ' ').split(' ');\n } else if (contains(date, DATES.NUM_MONTH_DAY_YEAR, false)) {\n [month, day, year] = date.replace(numDividerRegex, ' ').split(' ');\n } else if (contains(date, DATES.NUM_YEAR_MONTH_DAY, false)) {\n [year, month, day] = date.replace(numDividerRegex, ' ').split(' ');\n } else if (contains(date, DATES.TXT_DAY_MONTH_YEAR, false)) {\n [day, month, year] = date.replace(txtDividerRegex, ' ').split(' ');\n month = monthStringToNumber(month).toString();\n } else if (contains(date, DATES.TXT_MONTH_DAY_YEAR, false)) {\n [month, day, year] = date.replace(txtDividerRegex, ' ').split(' ');\n month = monthStringToNumber(month).toString();\n }\n\n if (!day || !month || !year) return null;\n\n return dayLite()\n .set({ day: parseInt(day), month: parseInt(month), year: parseInt(year) })\n .start('day')\n .toDate();\n}\n\nexport function dateStringToDateObj(matchingText: string): Date {\n const removedFillerWords = remove(matchingText, DATES.FILLER_WORDS);\n return dateStringToDayMonthYear(removedFillerWords);\n}\n","import { dateStringToDateObj } from './dates.helpers';\nimport DATES from './dates.constants';\nimport Filter from 'filters/filter';\n\nexport default class Dates extends Filter {\n constructor() {\n super(DATES.WITH_FILLER_WORDS, false);\n }\n\n parseStringToDateObj(match: string): Date {\n return dateStringToDateObj(match);\n }\n}\n","import DATES from 'filters/dates/dates.constants';\nimport DAY from 'filters/day/day.constants';\nimport MONTH from 'filters/month/month.constants';\nimport YEAR from 'filters/year/year.constants';\nimport { wrapInBracket } from 'utils/wrapInBracket';\n\nconst NUM_MONTH_YEAR = wrapInBracket([MONTH.NUMBERS, YEAR.ANY].join(DATES.NUM_DIVIDER));\nconst NUM_YEAR_MONTH = wrapInBracket([YEAR.ANY, MONTH.NUMBERS].join(DATES.NUM_DIVIDER));\nconst NUM_MONTH_DAY = wrapInBracket([MONTH.NUMBERS, DAY.NUMBERS].join(DATES.NUM_DIVIDER));\nconst NUM_DAY_MONTH = wrapInBracket([DAY.NUMBERS, MONTH.NUMBERS].join(DATES.NUM_DIVIDER));\nconst TXT_MONTH_DAY = wrapInBracket([MONTH.ANY, DAY.ANY].join(DATES.TXT_DIVIDER));\nconst TXT_DAY_MONTH = wrapInBracket([DAY.ANY, MONTH.ANY].join(DATES.TXT_DIVIDER));\nconst TXT_MONTH_YEAR = wrapInBracket([MONTH.ANY, YEAR.ANY].join(DATES.TXT_DIVIDER));\nconst TXT_YEAR_MONTH = wrapInBracket([YEAR.ANY, MONTH.ANY].join(DATES.TXT_DIVIDER));\nconst ANY = wrapInBracket(\n [\n NUM_MONTH_YEAR,\n NUM_YEAR_MONTH,\n NUM_MONTH_DAY,\n NUM_DAY_MONTH,\n TXT_MONTH_YEAR,\n TXT_MONTH_DAY,\n TXT_YEAR_MONTH,\n TXT_DAY_MONTH,\n ].join('|')\n);\n\nconst PARTIAL_DATES = {\n WITH_FILTER_WORDS: `(${DATES.FILLER_WORDS})?(${ANY})`,\n ANY,\n NUM_MONTH_YEAR,\n NUM_YEAR_MONTH,\n NUM_MONTH_DAY,\n NUM_DAY_MONTH,\n TXT_MONTH_YEAR,\n TXT_MONTH_DAY,\n TXT_DAY_MONTH,\n TXT_YEAR_MONTH,\n};\n\nexport default PARTIAL_DATES;\n","import { contains, remove } from 'lib/string/stringUtil';\nimport { dayLite } from 'lib/date/dayLite';\nimport { monthStringToNumber } from 'filters/month/month.helpers';\nimport PARTIAL_DATES from './partialDates.constants';\nimport DATES from 'filters/dates/dates.constants';\n\n/**\n * We want to return a future date, so if the month has already occurred this year, we give next year's date.\n */\nexport function getFutureYearIfDateIsInThePast(monthStr: string, dayStr: string): string {\n const currentDate = dayLite();\n const year = currentDate.year;\n const month = parseInt(monthStr, 10);\n const day = parseInt(dayStr, 10);\n const tempDate = dayLite().set({ month, day, year });\n if (tempDate < currentDate) {\n return (year + 1).toString();\n }\n return year.toString();\n}\n\nexport function getNextMonthIfDayIsInThePast(dayStr: string): number {\n const currentDate = dayLite();\n const day = parseInt(dayStr, 10);\n const currMonth = currentDate.month;\n const month = day < currentDate.day ? currMonth + 1 : currMonth;\n return month < 13 ? month : 1;\n}\n\nexport function partialDateStringToDayMonthYear(date: string): Date {\n let day: string;\n let month: string;\n let year: string;\n let dividerRegex: RegExp;\n\n if (contains(date, PARTIAL_DATES.NUM_MONTH_YEAR)) {\n dividerRegex = new RegExp(DATES.NUM_DIVIDER, 'g');\n [month, year] = date.replace(dividerRegex, ' ').split(' ');\n day = '1';\n } else if (contains(date, PARTIAL_DATES.NUM_YEAR_MONTH)) {\n dividerRegex = new RegExp(DATES.NUM_DIVIDER, 'g');\n [year, month] = date.replace(dividerRegex, ' ').split(' ');\n day = '1';\n } else if (contains(date, PARTIAL_DATES.NUM_DAY_MONTH)) {\n dividerRegex = new RegExp(DATES.NUM_DIVIDER, 'g');\n [day, month] = date.replace(dividerRegex, ' ').split(' ');\n year = getFutureYearIfDateIsInThePast(month, day);\n } else if (contains(date, PARTIAL_DATES.NUM_MONTH_DAY)) {\n dividerRegex = new RegExp(DATES.NUM_DIVIDER, 'g');\n [month, day] = date.replace(dividerRegex, ' ').split(' ');\n year = getFutureYearIfDateIsInThePast(month, day);\n } else if (contains(date, PARTIAL_DATES.TXT_MONTH_YEAR)) {\n dividerRegex = new RegExp(DATES.TXT_DIVIDER, 'g');\n [month, year] = date.replace(dividerRegex, ' ').split(' ');\n day = '1';\n month = monthStringToNumber(month).toString();\n } else if (contains(date, PARTIAL_DATES.TXT_YEAR_MONTH)) {\n dividerRegex = new RegExp(DATES.TXT_DIVIDER, 'g');\n [year, month] = date.replace(dividerRegex, ' ').split(' ');\n day = '1';\n month = monthStringToNumber(month).toString();\n } else if (contains(date, PARTIAL_DATES.TXT_DAY_MONTH)) {\n dividerRegex = new RegExp(DATES.TXT_DIVIDER, 'g');\n [day, month] = date.replace(dividerRegex, ' ').split(' ');\n month = monthStringToNumber(month).toString();\n year = getFutureYearIfDateIsInThePast(month, day);\n } else if (contains(date, PARTIAL_DATES.TXT_MONTH_DAY)) {\n dividerRegex = new RegExp(DATES.TXT_DIVIDER, 'g');\n [month, day] = date.replace(dividerRegex, ' ').split(' ');\n month = monthStringToNumber(month).toString();\n year = getFutureYearIfDateIsInThePast(month, day);\n }\n\n if (!day || !month || !year) return null;\n\n return dayLite()\n .set({ day: parseInt(day), month: parseInt(month), year: parseInt(year) })\n .start('day')\n .toDate();\n}\n\nexport function partialDateStringToDateObj(matchingText: string): Date {\n const removedFillerWords = remove(matchingText, DATES.FILLER_WORDS);\n return partialDateStringToDayMonthYear(removedFillerWords);\n}\n","import DATES from 'filters/dates/dates.constants';\nimport { wrapInBracket } from 'utils/wrapInBracket';\n\nconst TODAY = 'today';\nconst TOMORROW = 'tomorrow';\nconst YESTERDAY = 'yesterday';\n\nconst RELATIVE_ADVERB = {\n ANY: wrapInBracket([TODAY, TOMORROW, YESTERDAY].join('|')),\n TODAY,\n TOMORROW,\n YESTERDAY,\n};\n\nconst RELATIVE_PREPOSITIONS = '((in|after) )?';\nconst RELATIVE_POSTPOSITIONS = '( (from now|from today|later|after))';\n\nconst DAYS = '(days|day)';\nconst WEEKS = '(weeks|week|wks|wk)';\nconst MONTHS = '(months|month)';\nconst YEARS = '(years|year|yrs|yr)';\n\nconst TIME_UNITS = {\n DAYS,\n WEEKS,\n MONTHS,\n YEARS,\n ANY: wrapInBracket([DAYS, WEEKS, MONTHS, YEARS].join('|')),\n};\n\nconst NEXT = '(next|following)';\nconst ONE = 'a';\n\nconst VERBAL_QUANTIFIERS = {\n ANY: [NEXT, ONE].join('|'),\n NEXT,\n ONE,\n};\n\nconst ARGUMENT = `([0-9]+|${VERBAL_QUANTIFIERS.ANY})( )${TIME_UNITS.ANY}`;\nconst ARGUMENT_AFTER = `${RELATIVE_PREPOSITIONS}?${ARGUMENT}`;\nconst ARGUMENT_FIRST = `${ARGUMENT}${RELATIVE_POSTPOSITIONS}`;\n\nconst RELATIVE_EXPRESSION = {\n ANY: [ARGUMENT_FIRST, ARGUMENT_AFTER].join('|'),\n ARGUMENT_AFTER,\n ARGUMENT_FIRST,\n};\nconst ANY = wrapInBracket([RELATIVE_ADVERB.ANY, RELATIVE_EXPRESSION.ANY].join('|'));\nconst FILLER_WORDS = [RELATIVE_PREPOSITIONS, RELATIVE_POSTPOSITIONS].join('|');\n\nconst RELATIVE_DATES = {\n WITH_FILLER_WORDS: `(${DATES.FILLER_WORDS})?${ANY}`,\n ANY,\n RELATIVE_ADVERB,\n RELATIVE_EXPRESSION,\n FILLER_WORDS,\n TIME_UNITS,\n VERBAL_QUANTIFIERS,\n};\n\nexport default RELATIVE_DATES;\n","import RELATIVE_DATES from 'filters/relativeDates/relativeDates.constants';\nimport { dayLite } from 'lib/date/dayLite';\nimport { DayLiteUnits } from 'lib/date/types';\nimport { contains, matchPattern, remove } from 'lib/string/stringUtil';\nimport findMatchingKey from 'utils/findMatchingKey';\n\nexport type RegexTimeUnit = keyof typeof RELATIVE_DATES.TIME_UNITS;\n\nexport function regexTimeUnitToDayLiteTimeUnit(regexTimeUnit: RegexTimeUnit): DayLiteUnits {\n switch (regexTimeUnit) {\n case 'DAYS':\n return 'day';\n case 'WEEKS':\n return 'week';\n case 'MONTHS':\n return 'month';\n case 'YEARS':\n return 'year';\n case 'ANY':\n return null;\n }\n}\n\nexport function convertRelativeAdverbToObj(relativeDateStr: string): Date {\n if (contains(relativeDateStr, RELATIVE_DATES.RELATIVE_ADVERB.TODAY)) {\n return dayLite().start('day').toDate();\n }\n\n if (contains(relativeDateStr, RELATIVE_DATES.RELATIVE_ADVERB.YESTERDAY)) {\n return dayLite().minus(1, 'day').start('day').toDate();\n }\n\n return dayLite().plus(1, 'day').start('day').toDate();\n}\n\nexport function convertRelativeExpressionToObj(expression: string): Date {\n const match = matchPattern(expression, RELATIVE_DATES.TIME_UNITS.ANY);\n\n if (!match) return null;\n\n const [timeUnit] = match;\n const unit = findMatchingKey(RELATIVE_DATES.TIME_UNITS, timeUnit) as RegexTimeUnit;\n\n if (!unit) return null;\n\n const dayLiteTimeUnit = regexTimeUnitToDayLiteTimeUnit(unit);\n const period = remove(expression, unit);\n let quantity: number;\n\n if (contains(period, RELATIVE_DATES.VERBAL_QUANTIFIERS.ONE)) {\n quantity = 1;\n } else if (contains(period, RELATIVE_DATES.VERBAL_QUANTIFIERS.NEXT)) {\n return dayLite().start(dayLiteTimeUnit).next(1, dayLiteTimeUnit).toDate();\n } else {\n quantity = parseInt(period, 10);\n }\n\n const test = dayLite().plus(quantity, dayLiteTimeUnit).start('day').toDate();\n\n return test;\n}\n\nexport function relativeDateStringToDayMonthYear(date: string): Date {\n const removedFillerWords = remove(date, RELATIVE_DATES.FILLER_WORDS);\n if (contains(removedFillerWords, RELATIVE_DATES.RELATIVE_ADVERB.ANY)) {\n return convertRelativeAdverbToObj(removedFillerWords);\n } else {\n return convertRelativeExpressionToObj(removedFillerWords);\n }\n}\n\nexport function relativeDateStringToDateObj(matchingText: string): Date {\n const dateObj = relativeDateStringToDayMonthYear(matchingText);\n if (!dateObj) return null;\n return dayLite(dateObj).start('day').toDate();\n}\n","import { contains } from 'lib/string/stringUtil';\n\nexport default function findMatchingKey(object: Record<string, string>, pattern: string): string {\n const keys = Object.keys(object);\n\n if (keys.length) {\n for (const key of keys) {\n if (contains(object[key], pattern)) return key;\n }\n }\n\n return null;\n}\n","import DATES from 'filters/dates/dates.constants';\nimport PARTIAL_DATES from 'filters/partialDates/partialDates.constants';\nimport RELATIVE_DATES from 'filters/relativeDates/relativeDates.constants';\nimport TIME from 'filters/time/time.constants';\n\nconst DATE_PART = `(${DATES.FILLER_WORDS})?(${[DATES.ANY, PARTIAL_DATES.ANY, RELATIVE_DATES.ANY].join('|')})`;\nconst TIME_PART = `(${TIME.FILLER_WORDS}( ))?${TIME.ANY}`;\nconst DATE_FIRST = [DATE_PART, TIME_PART].join(' ');\nconst TIME_FIRST = [TIME_PART, DATE_PART].join(' ');\n\nconst DATE_AND_TIME = {\n ANY: [DATE_FIRST, TIME_FIRST].join('|'),\n};\n\nexport default DATE_AND_TIME;\n","import { dateAndTimeToDateObj } from './dateAndTime.helpers';\nimport DATE_AND_TIME from './dateAndTime.constants';\nimport Filter from 'filters/filter';\n\nexport default class DateAndTime extends Filter {\n constructor() {\n super(DATE_AND_TIME.ANY, false);\n }\n\n parseStringToDateObj(match: string): Date {\n return dateAndTimeToDateObj(match);\n }\n}\n","import DATES from 'filters/dates/dates.constants';\nimport { dateStringToDayMonthYear } from 'filters/dates/dates.helpers';\nimport PARTIAL_DATES from 'filters/partialDates/partialDates.constants';\nimport { partialDateStringToDayMonthYear } from 'filters/partialDates/partialDates.helpers';\nimport { relativeDateStringToDayMonthYear } from 'filters/relativeDates/relativeDates.helpers';\nimport RELATIVE_DATES from 'filters/relativeDates/relativeDates.constants';\nimport TIME from 'filters/time/time.constants';\nimport { timeStringToDateObj } from 'filters/time/time.helpers';\nimport { dayLite } from 'lib/date/dayLite';\nimport { contains, matchPattern, remove } from 'lib/string/stringUtil';\nimport { DateObjectSchema } from 'serina.schema';\n\nexport function getDateString(matchingText: string) {\n const stringWithoutDateFillerWords = remove(matchingText, DATES.FILLER_WORDS);\n const dateStringMatches = matchPattern(\n stringWithoutDateFillerWords,\n `(${DATES.ANY}|${PARTIAL_DATES.ANY}|${RELATIVE_DATES.ANY})`\n );\n\n if (!dateStringMatches) return null;\n\n return dateStringMatches[0];\n}\n\nexport function getTimeString(matchingText: string) {\n const stringWithoutTimeFillerWords = remove(matchingText, TIME.FILLER_WORDS);\n const timeStringMatches = matchPattern(stringWithoutTimeFillerWords, TIME.ANY);\n\n if (!timeStringMatches) return null;\n\n return timeStringMatches[0];\n}\n\nexport function differentDateStringToObj(dateString: string): DateObjectSchema {\n let dateObj: DateObjectSchema;\n if (contains(dateString, DATES.ANY)) {\n const { day, month, year } = dayLite(dateStringToDayMonthYear(dateString));\n dateObj = { day, month, year };\n } else if (contains(dateString, `${PARTIAL_DATES.ANY}`)) {\n const { day, month, year } = dayLite(partialDateStringToDayMonthYear(dateString));\n dateObj = { day, month, year };\n } else {\n const { day, nativeMonth