UNPKG

chrono-node

Version:

A natural language date parser in Javascript

232 lines 8.49 kB
import quarterOfYear from "dayjs/plugin/quarterOfYear.js"; import dayjs from "dayjs"; import { assignSimilarDate, assignSimilarTime, implySimilarTime } from "./utils/dayjs.js"; import { toTimezoneOffset } from "./timezone.js"; dayjs.extend(quarterOfYear); export class ReferenceWithTimezone { constructor(input) { input = input ?? new Date(); if (input instanceof Date) { this.instant = input; } else { this.instant = input.instant ?? new Date(); this.timezoneOffset = toTimezoneOffset(input.timezone, this.instant); } } getDateWithAdjustedTimezone() { return new Date(this.instant.getTime() + this.getSystemTimezoneAdjustmentMinute(this.instant) * 60000); } getSystemTimezoneAdjustmentMinute(date, overrideTimezoneOffset) { if (!date || date.getTime() < 0) { date = new Date(); } const currentTimezoneOffset = -date.getTimezoneOffset(); const targetTimezoneOffset = overrideTimezoneOffset ?? this.timezoneOffset ?? currentTimezoneOffset; return currentTimezoneOffset - targetTimezoneOffset; } } export class ParsingComponents { constructor(reference, knownComponents) { this._tags = new Set(); this.reference = reference; this.knownValues = {}; this.impliedValues = {}; if (knownComponents) { for (const key in knownComponents) { this.knownValues[key] = knownComponents[key]; } } const refDayJs = dayjs(reference.instant); this.imply("day", refDayJs.date()); this.imply("month", refDayJs.month() + 1); this.imply("year", refDayJs.year()); this.imply("hour", 12); this.imply("minute", 0); this.imply("second", 0); this.imply("millisecond", 0); } get(component) { if (component in this.knownValues) { return this.knownValues[component]; } if (component in this.impliedValues) { return this.impliedValues[component]; } return null; } isCertain(component) { return component in this.knownValues; } getCertainComponents() { return Object.keys(this.knownValues); } imply(component, value) { if (component in this.knownValues) { return this; } this.impliedValues[component] = value; return this; } assign(component, value) { this.knownValues[component] = value; delete this.impliedValues[component]; return this; } delete(component) { delete this.knownValues[component]; delete this.impliedValues[component]; } clone() { const component = new ParsingComponents(this.reference); component.knownValues = {}; component.impliedValues = {}; for (const key in this.knownValues) { component.knownValues[key] = this.knownValues[key]; } for (const key in this.impliedValues) { component.impliedValues[key] = this.impliedValues[key]; } return component; } isOnlyDate() { return !this.isCertain("hour") && !this.isCertain("minute") && !this.isCertain("second"); } isOnlyTime() { return (!this.isCertain("weekday") && !this.isCertain("day") && !this.isCertain("month") && !this.isCertain("year")); } isOnlyWeekdayComponent() { return this.isCertain("weekday") && !this.isCertain("day") && !this.isCertain("month"); } isDateWithUnknownYear() { return this.isCertain("month") && !this.isCertain("year"); } isValidDate() { const date = this.dateWithoutTimezoneAdjustment(); if (date.getFullYear() !== this.get("year")) return false; if (date.getMonth() !== this.get("month") - 1) return false; if (date.getDate() !== this.get("day")) return false; if (this.get("hour") != null && date.getHours() != this.get("hour")) return false; if (this.get("minute") != null && date.getMinutes() != this.get("minute")) return false; return true; } toString() { return `[ParsingComponents { tags: ${JSON.stringify(Array.from(this._tags).sort())}, knownValues: ${JSON.stringify(this.knownValues)}, impliedValues: ${JSON.stringify(this.impliedValues)}}, reference: ${JSON.stringify(this.reference)}]`; } dayjs() { return dayjs(this.date()); } date() { const date = this.dateWithoutTimezoneAdjustment(); const timezoneAdjustment = this.reference.getSystemTimezoneAdjustmentMinute(date, this.get("timezoneOffset")); return new Date(date.getTime() + timezoneAdjustment * 60000); } addTag(tag) { this._tags.add(tag); return this; } addTags(tags) { for (const tag of tags) { this._tags.add(tag); } return this; } tags() { return new Set(this._tags); } dateWithoutTimezoneAdjustment() { const date = new Date(this.get("year"), this.get("month") - 1, this.get("day"), this.get("hour"), this.get("minute"), this.get("second"), this.get("millisecond")); date.setFullYear(this.get("year")); return date; } static createRelativeFromReference(reference, fragments) { let date = dayjs(reference.instant); for (const key in fragments) { date = date.add(fragments[key], key); } const components = new ParsingComponents(reference); if (fragments["hour"] || fragments["minute"] || fragments["second"]) { assignSimilarTime(components, date); assignSimilarDate(components, date); if (reference.timezoneOffset !== null) { components.assign("timezoneOffset", -reference.instant.getTimezoneOffset()); } } else { implySimilarTime(components, date); if (reference.timezoneOffset !== null) { components.imply("timezoneOffset", -reference.instant.getTimezoneOffset()); } if (fragments["d"]) { components.assign("day", date.date()); components.assign("month", date.month() + 1); components.assign("year", date.year()); } else if (fragments["week"]) { components.assign("day", date.date()); components.assign("month", date.month() + 1); components.assign("year", date.year()); components.imply("weekday", date.day()); } else { components.imply("day", date.date()); if (fragments["month"]) { components.assign("month", date.month() + 1); components.assign("year", date.year()); } else { components.imply("month", date.month() + 1); if (fragments["year"]) { components.assign("year", date.year()); } else { components.imply("year", date.year()); } } } } return components; } } export class ParsingResult { constructor(reference, index, text, start, end) { this.reference = reference; this.refDate = reference.instant; this.index = index; this.text = text; this.start = start || new ParsingComponents(reference); this.end = end; } clone() { const result = new ParsingResult(this.reference, this.index, this.text); result.start = this.start ? this.start.clone() : null; result.end = this.end ? this.end.clone() : null; return result; } date() { return this.start.date(); } tags() { const combinedTags = new Set(this.start.tags()); if (this.end) { for (const tag of this.end.tags()) { combinedTags.add(tag); } } return combinedTags; } toString() { const tags = Array.from(this.tags()).sort(); return `[ParsingResult {index: ${this.index}, text: '${this.text}', tags: ${JSON.stringify(tags)} ...}]`; } } //# sourceMappingURL=results.js.map