chrono-node
Version:
A natural language date parser in Javascript
411 lines (355 loc) • 18.3 kB
text/typescript
import * as chrono from "../../src";
import { Month, Weekday } from "../../src/types";
import { getLastWeekdayOfMonth } from "../../src/timezone";
import { testSingleCase } from "../test_util";
import { Meridiem } from "../../src";
test("Test - Parsing date/time with UTC offset", function () {
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am utc+02:45 ", (result, text) => {
expect(result.text).toBe("wednesday, september 16, 2020 at 11 am utc+02:45");
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
expect(result.start.get("timezoneOffset")).toBe(2 * 60 + 45);
});
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am utc+0245 ", (result, text) => {
expect(result.text).toBe("wednesday, september 16, 2020 at 11 am utc+0245");
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
expect(result.start.get("timezoneOffset")).toBe(2 * 60 + 45);
});
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am utc+02 ", (result, text) => {
expect(result.text).toBe("wednesday, september 16, 2020 at 11 am utc+02");
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
expect(result.start.get("timezoneOffset")).toBe(2 * 60);
});
});
test("Test - Parsing date/time with numeric offset", () => {
testSingleCase(chrono, "wednesday, september 16, 2020 at 23.00+14", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.isCertain("timezoneOffset")).toBe(true);
expect(result.start.get("timezoneOffset")).toBe(14 * 60);
});
testSingleCase(chrono, "wednesday, september 16, 2020 at 23.00+1400", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.isCertain("timezoneOffset")).toBe(true);
expect(result.start.get("timezoneOffset")).toBe(14 * 60);
});
testSingleCase(chrono, "wednesday, september 16, 2020 at 23.00+15", (result, text) => {
expect(result.text).toBe("wednesday, september 16, 2020 at 23.00");
expect(result.start.isCertain("timezoneOffset")).toBe(false);
});
});
test("Test - Parsing date/time with GMT offset", function () {
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am GMT -08:45 ", (result, text) => {
expect(result.text).toBe("wednesday, september 16, 2020 at 11 am GMT -08:45");
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
expect(result.start.get("timezoneOffset")).toBe(-(8 * 60 + 45));
});
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am gmt+02 ", (result, text) => {
expect(result.text).toBe("wednesday, september 16, 2020 at 11 am gmt+02");
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
expect(result.start.get("timezoneOffset")).toBe(2 * 60);
});
testSingleCase(chrono, "published: 10:30 (gmt-2:30).", (result, text) => {
expect(result.text).toBe("10:30 (gmt-2:30)");
expect(result.start.get("hour")).toBe(10);
expect(result.start.get("minute")).toBe(30);
expect(result.start.get("timezoneOffset")).toBe(-(2 * 60 + 30));
});
});
test("Test - Parsing date/time with timezone abbreviation", function () {
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("year")).toBe(2020);
expect(result.start.get("month")).toBe(9);
expect(result.start.get("day")).toBe(16);
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
expect(result.start.get("timezoneOffset")).toBe(null);
});
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am JST", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("year")).toBe(2020);
expect(result.start.get("month")).toBe(9);
expect(result.start.get("day")).toBe(16);
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
// JST: GMT+9:00
expect(result.start.get("timezoneOffset")).toBe(9 * 60);
});
testSingleCase(chrono, "wednesday, september 16, 2020 at 11 am GMT+0900 (JST)", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("year")).toBe(2020);
expect(result.start.get("month")).toBe(9);
expect(result.start.get("day")).toBe(16);
expect(result.start.get("hour")).toBe(11);
expect(result.start.get("minute")).toBe(0);
// JST: GMT+9:00
expect(result.start.get("timezoneOffset")).toBe(9 * 60);
});
testSingleCase(chrono, "10:30 pst today", new Date(2016, 10 - 1, 1, 8), (result, text) => {
expect(result.text).toBe("10:30 pst today");
expect(result.start.get("year")).toBe(2016);
expect(result.start.get("month")).toBe(10);
expect(result.start.get("day")).toBe(1);
expect(result.start.get("hour")).toBe(10);
expect(result.start.get("minute")).toBe(30);
// PST: UTC−08:00
expect(result.start.get("timezoneOffset")).toBe(-8 * 60);
});
});
test("Test - Parsing date range with timezone abbreviation", function () {
testSingleCase(chrono, "10:30 JST today to 10:30 pst tomorrow ", new Date(2016, 10 - 1, 1, 8), (result, text) => {
expect(result.text).toBe("10:30 JST today to 10:30 pst tomorrow");
expect(result.start.get("year")).toBe(2016);
expect(result.start.get("month")).toBe(10);
expect(result.start.get("day")).toBe(1);
expect(result.start.get("hour")).toBe(10);
expect(result.start.get("minute")).toBe(30);
expect(result.start.get("timezoneOffset")).toBe(9 * 60); // JST: GMT+9:00
expect(result.end.get("year")).toBe(2016);
expect(result.end.get("month")).toBe(10);
expect(result.end.get("day")).toBe(2);
expect(result.end.get("hour")).toBe(10);
expect(result.end.get("minute")).toBe(30);
expect(result.end.get("timezoneOffset")).toBe(-8 * 60); // PST: UTC−08:00
});
// TODO: Support "10:30 JST today - 10:30 pst tomorrow" case.
// Currently, this is not possible because "today - 10:30" is recognized as "today 10:30" (not range)
});
test("Test - Parsing date/time with ambiguous timezone abbreviations", function () {
// Test dates around transition to DST for ET
testSingleCase(chrono, "2022-03-12 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-5 * 60); // ET during standard time (EST) in 2022
});
testSingleCase(chrono, "2022-03-13 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-4 * 60); // ET during DST (EDT) in 2022
});
testSingleCase(chrono, "2021-03-13 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-5 * 60); // ET during standard time (EST) in 2021
});
testSingleCase(chrono, "2021-03-14 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-4 * 60); // ET during DST (EDT) in 2021
});
// Also test around transition *from* DST
testSingleCase(chrono, "2021-11-06 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-4 * 60); // ET during DST (EDT) in 2021
});
testSingleCase(chrono, "2021-11-07 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-5 * 60); // ET during standard time (EST) in 2021
});
testSingleCase(chrono, "2020-10-31 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-4 * 60); // ET during DST (EDT) in 2020
});
testSingleCase(chrono, "2020-11-01 23:00 ET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(-5 * 60); // ET during standard time (EST) in 2020
});
// Same checks, but for CET (which transitions at different dates with different rules)
// Test dates around transition to DST for CET
testSingleCase(chrono, "2022-03-26 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(60); // CET during standard time (CET) in 2022
});
testSingleCase(chrono, "2022-03-27 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(2 * 60); // CET during DST (CEST) in 2022
});
testSingleCase(chrono, "2021-03-27 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(60); // CET during standard time (CET) in 2021
});
testSingleCase(chrono, "2021-03-28 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(2 * 60); // CET during DST (CEST) in 2021
});
// Also test around transition *from* DST for CET
testSingleCase(chrono, "2022-10-29 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(2 * 60); // CET during DST (CEST) in 2022
});
testSingleCase(chrono, "2022-10-30 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(60); // CET during standard time (CET) in 2022
});
testSingleCase(chrono, "2021-10-30 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(2 * 60); // CET during DST (CEST) in 2021
});
testSingleCase(chrono, "2021-10-31 23:00 CET", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("hour")).toBe(23);
expect(result.start.get("timezoneOffset")).toBe(60); // CET during standard time (CET) in 2021
});
});
test("Test - Timezone parsing overrides", function () {
// chrono.parse('at 10:00 XYZ', new Date(2023, 3, 20), {timezones: {XYZ: -180}})
// XYZ shouldn't be recognized or parsed as a timezone
testSingleCase(chrono, "Jan 1st 2023 at 10:00 XYZ", (result) => {
expect(result.text).toBe("Jan 1st 2023 at 10:00");
expect(result.start).toBeDate(new Date(2023, 1 - 1, 1, 10));
});
// Parse the correct tzoffset when XYZ is provided as a custom tz in parsingOptions
testSingleCase(
chrono,
"Jan 1st 2023 at 10:00 XYZ",
new Date(2023, 1, 1),
{ timezones: { XYZ: -180 } },
(result) => {
expect(result.text).toBe("Jan 1st 2023 at 10:00 XYZ");
expect(result.start.get("timezoneOffset")).toBe(-180);
}
);
// Parse the correct tzoffset when XYZ is provided as a custom ambiguous tz in parsingOptions
const parseXYZAsAmbiguousTz = {
timezoneOffsetDuringDst: -120,
timezoneOffsetNonDst: -180,
dstStart: (year: number) => getLastWeekdayOfMonth(year, Month.MARCH, Weekday.SUNDAY, 2),
dstEnd: (year: number) => getLastWeekdayOfMonth(year, Month.OCTOBER, Weekday.SUNDAY, 3),
};
// Parsing a non-DST date
testSingleCase(
chrono,
"Jan 1st 2023 at 10:00 XYZ",
new Date(2023, 1, 1),
{ timezones: { XYZ: parseXYZAsAmbiguousTz } },
(result) => {
expect(result.text).toBe("Jan 1st 2023 at 10:00 XYZ");
expect(result.start.get("timezoneOffset")).toBe(-180);
}
);
// Parsing a DST date
testSingleCase(
chrono,
"Jun 1st 2023 at 10:00 XYZ",
new Date(2023, 1, 1),
{ timezones: { XYZ: parseXYZAsAmbiguousTz } },
(result) => {
expect(result.text).toBe("Jun 1st 2023 at 10:00 XYZ");
expect(result.start.get("timezoneOffset")).toBe(-120);
}
);
});
test("Test - Parsing date with timezone abbreviation", function () {
testSingleCase(chrono, "Wednesday, September 16, 2020, EST", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("timezoneOffset")).toBe(-300);
});
});
test("Test - Not parsing timezone from relative time", function () {
{
const refInstant = new Date("Sun Nov 29 2020 13:24:13 GMT+0900 (Japan Standard Time)");
const expectedInstant = new Date("Sun Nov 29 2020 14:24:13 GMT+0900 (Japan Standard Time)");
testSingleCase(chrono, "in 1 hour get eggs and milk", refInstant, (result, text) => {
expect(result.text).toBe("in 1 hour");
expect(result.start.get("timezoneOffset")).toBe(-refInstant.getTimezoneOffset());
expect(result.start).toBeDate(expectedInstant);
});
}
{
const refInstant = new Date("Sun Nov 29 2020 13:24:13 GMT+0900 (Japan Standard Time)");
const expectedInstant = new Date("Sun Nov 29 2020 14:24:13 GMT+0900 (Japan Standard Time)");
testSingleCase(chrono, "in 1 hour GMT", refInstant, (result, text) => {
// expect(result.text).toBe("in 1 hour"); known issue when running test in the GMT time
expect(result.start).toBeDate(expectedInstant);
});
}
{
const refInstant = new Date("Sun Nov 29 2020 13:24:13 GMT+0900 (Japan Standard Time)");
const expectedInstant = new Date("Sun Nov 29 2020 14:24:13 GMT+0900 (Japan Standard Time)");
testSingleCase(chrono, "in 1 hour GMT", { instant: refInstant, timezone: "JST" }, (result, text) => {
// expect(result.text).toBe("in 1 hour");
expect(result.start).toBeDate(expectedInstant);
});
}
{
const refInstant = new Date("Sun Nov 29 2020 13:24:13 GMT+0900 (Japan Standard Time)");
const expectedInstant = new Date("Sun Nov 29 2020 14:24:13 GMT+0900 (Japan Standard Time)");
testSingleCase(chrono, "in 1 hour GMT", { instant: refInstant, timezone: "BST" }, (result, text) => {
// expect(result.text).toBe("in 1 hour");
expect(result.start).toBeDate(expectedInstant);
});
}
});
test("Test - Relative time (Now) is not effected by timezone setting", function () {
const refInstant = new Date(1637674343000);
testSingleCase(chrono, "now", { instant: refInstant }, (result) => {
expect(result.text).toBe("now");
expect(result.start).toBeDate(refInstant);
});
testSingleCase(chrono, "now", { instant: refInstant, timezone: null }, (result) => {
expect(result.text).toBe("now");
expect(result.start).toBeDate(refInstant);
});
testSingleCase(chrono, "now", { instant: refInstant, timezone: "BST" }, (result) => {
expect(result.text).toBe("now");
expect(result.start).toBeDate(refInstant);
});
testSingleCase(chrono, "now", { instant: refInstant, timezone: "JST" }, (result) => {
expect(result.text).toBe("now");
expect(result.start).toBeDate(refInstant);
});
});
test("Test - Relative time (2 hour later) is not effected by timezone setting", function () {
const refInstant = new Date(1637674343000);
const expectedInstant = new Date(1637674343000 + 2 * 60 * 60 * 1000);
testSingleCase(chrono, "2 hour later", { instant: refInstant }, (result) => {
expect(result.text).toBe("2 hour later");
expect(result.start).toBeDate(expectedInstant);
});
testSingleCase(chrono, "2 hour later", { instant: refInstant, timezone: null }, (result) => {
expect(result.text).toBe("2 hour later");
expect(result.start).toBeDate(expectedInstant);
});
testSingleCase(chrono, "2 hour later", { instant: refInstant, timezone: "BST" }, (result) => {
expect(result.text).toBe("2 hour later");
expect(result.start).toBeDate(expectedInstant);
});
testSingleCase(chrono, "2 hour later", { instant: refInstant, timezone: "JST" }, (result) => {
expect(result.text).toBe("2 hour later");
expect(result.start).toBeDate(expectedInstant);
});
});
test("Test - Parsing timezone from relative date when valid", function () {
const refDate = new Date(2020, 11 - 1, 14, 13, 48, 22);
testSingleCase(chrono, "in 1 day get eggs and milk", refDate, (result, text) => {
expect(result.text).toBe("in 1 day");
expect(result.start.get("timezoneOffset")).toBe(-refDate.getTimezoneOffset());
});
testSingleCase(chrono, "in 1 day GET", refDate, (result, text) => {
expect(result.text).toBe("in 1 day GET");
expect(result.start.get("timezoneOffset")).toBe(240);
});
testSingleCase(chrono, "today EST", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("timezoneOffset")).toBe(-300);
});
testSingleCase(chrono, "next week EST", (result, text) => {
expect(result.text).toBe(text);
expect(result.start.get("timezoneOffset")).toBe(-300);
});
});