chrono-node
Version:
A natural language date parser in Javascript
232 lines • 8.49 kB
JavaScript
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