chrono-node 2.3.6 → 2.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/locales/en/index.js +2 -1
- package/dist/locales/en/parsers/ENRelativeDateFormatParser.js +2 -2
- package/dist/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js +1 -1
- package/dist/locales/en/refiners/ENMergeRelativeDateRefiner.d.ts +7 -0
- package/dist/locales/en/refiners/ENMergeRelativeDateRefiner.js +35 -0
- package/dist/timezone.js +1 -1
- package/package.json +1 -1
- package/src/locales/en/index.ts +2 -1
- package/src/locales/en/parsers/ENRelativeDateFormatParser.ts +2 -2
- package/src/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.ts +1 -1
- package/src/locales/en/refiners/ENMergeRelativeDateRefiner.ts +58 -0
- package/src/timezone.ts +1 -1
- package/test/en/en_merging_relative_dates.test.ts +51 -0
- package/test/en/en_relative.test.ts +20 -0
- package/test/en/en_time_units_casual_relative.test.ts +16 -0
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ For example:
|
|
|
133
133
|
```js
|
|
134
134
|
const results = chrono.parse('I have an appointment tomorrow from 10 to 11 AM');
|
|
135
135
|
|
|
136
|
-
results[0].index; //
|
|
136
|
+
results[0].index; // 22
|
|
137
137
|
results[0].text; // 'tomorrow from 10 to 11 AM'
|
|
138
138
|
results[0].refDate; // Sat Dec 13 2014 21:50:14 GMT-0600 (CST)
|
|
139
139
|
|
package/dist/locales/en/index.js
CHANGED
|
@@ -23,6 +23,7 @@ const ENRelativeDateFormatParser_1 = __importDefault(require("./parsers/ENRelati
|
|
|
23
23
|
const chrono_1 = require("../../chrono");
|
|
24
24
|
const SlashDateFormatParser_1 = __importDefault(require("../../common/parsers/SlashDateFormatParser"));
|
|
25
25
|
const ENTimeUnitCasualRelativeFormatParser_1 = __importDefault(require("./parsers/ENTimeUnitCasualRelativeFormatParser"));
|
|
26
|
+
const ENMergeRelativeDateRefiner_1 = __importDefault(require("./refiners/ENMergeRelativeDateRefiner"));
|
|
26
27
|
exports.casual = new chrono_1.Chrono(createCasualConfiguration(false));
|
|
27
28
|
exports.strict = new chrono_1.Chrono(createConfiguration(true, false));
|
|
28
29
|
exports.GB = new chrono_1.Chrono(createConfiguration(false, true));
|
|
@@ -58,7 +59,7 @@ function createConfiguration(strictMode = true, littleEndian = false) {
|
|
|
58
59
|
new ENTimeUnitAgoFormatParser_1.default(strictMode),
|
|
59
60
|
new ENTimeUnitLaterFormatParser_1.default(strictMode),
|
|
60
61
|
],
|
|
61
|
-
refiners: [new ENMergeDateTimeRefiner_1.default(), new ENMergeDateRangeRefiner_1.default()],
|
|
62
|
+
refiners: [new ENMergeRelativeDateRefiner_1.default(), new ENMergeDateTimeRefiner_1.default(), new ENMergeDateRangeRefiner_1.default()],
|
|
62
63
|
}, strictMode);
|
|
63
64
|
}
|
|
64
65
|
exports.createConfiguration = createConfiguration;
|
|
@@ -8,7 +8,7 @@ const results_1 = require("../../../results");
|
|
|
8
8
|
const dayjs_1 = __importDefault(require("dayjs"));
|
|
9
9
|
const AbstractParserWithWordBoundary_1 = require("../../../common/parsers/AbstractParserWithWordBoundary");
|
|
10
10
|
const pattern_1 = require("../../../utils/pattern");
|
|
11
|
-
const PATTERN = new RegExp(`(this|
|
|
11
|
+
const PATTERN = new RegExp(`(this|last|past|next|after\\s*this)\\s*(${pattern_1.matchAnyPattern(constants_1.TIME_UNIT_DICTIONARY)})(?=\\s*)` + "(?=\\W|$)", "i");
|
|
12
12
|
const MODIFIER_WORD_GROUP = 1;
|
|
13
13
|
const RELATIVE_WORD_GROUP = 2;
|
|
14
14
|
class ENRelativeDateFormatParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {
|
|
@@ -19,7 +19,7 @@ class ENRelativeDateFormatParser extends AbstractParserWithWordBoundary_1.Abstra
|
|
|
19
19
|
const modifier = match[MODIFIER_WORD_GROUP].toLowerCase();
|
|
20
20
|
const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();
|
|
21
21
|
const timeunit = constants_1.TIME_UNIT_DICTIONARY[unitWord];
|
|
22
|
-
if (modifier == "next") {
|
|
22
|
+
if (modifier == "next" || modifier.startsWith("after")) {
|
|
23
23
|
const timeUnits = {};
|
|
24
24
|
timeUnits[timeunit] = 1;
|
|
25
25
|
return results_1.ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
@@ -4,7 +4,7 @@ const constants_1 = require("../constants");
|
|
|
4
4
|
const results_1 = require("../../../results");
|
|
5
5
|
const AbstractParserWithWordBoundary_1 = require("../../../common/parsers/AbstractParserWithWordBoundary");
|
|
6
6
|
const timeunits_1 = require("../../../utils/timeunits");
|
|
7
|
-
const PATTERN = new RegExp(`(this|last|past|next|\\+|-)\\s*(${constants_1.TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
|
|
7
|
+
const PATTERN = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${constants_1.TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
|
|
8
8
|
class ENTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {
|
|
9
9
|
innerPattern() {
|
|
10
10
|
return PATTERN;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MergingRefiner } from "../../../common/abstractRefiners";
|
|
2
|
+
import { ParsingResult } from "../../../results";
|
|
3
|
+
export default class ENMergeRelativeDateRefiner extends MergingRefiner {
|
|
4
|
+
patternBetween(): RegExp;
|
|
5
|
+
shouldMergeResults(textBetween: string, currentResult: ParsingResult, nextResult: ParsingResult): boolean;
|
|
6
|
+
mergeResults(textBetween: string, currentResult: ParsingResult, nextResult: ParsingResult): ParsingResult;
|
|
7
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const abstractRefiners_1 = require("../../../common/abstractRefiners");
|
|
4
|
+
const results_1 = require("../../../results");
|
|
5
|
+
const constants_1 = require("../constants");
|
|
6
|
+
const timeunits_1 = require("../../../utils/timeunits");
|
|
7
|
+
function hasImpliedEarlierReferenceDate(result) {
|
|
8
|
+
return result.text.match(/\s+(before|from)$/i) != null;
|
|
9
|
+
}
|
|
10
|
+
function hasImpliedLaterReferenceDate(result) {
|
|
11
|
+
return result.text.match(/\s+(after|since)$/i) != null;
|
|
12
|
+
}
|
|
13
|
+
class ENMergeRelativeDateRefiner extends abstractRefiners_1.MergingRefiner {
|
|
14
|
+
patternBetween() {
|
|
15
|
+
return /^\s*$/i;
|
|
16
|
+
}
|
|
17
|
+
shouldMergeResults(textBetween, currentResult, nextResult) {
|
|
18
|
+
if (!textBetween.match(this.patternBetween())) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (!hasImpliedEarlierReferenceDate(currentResult) && !hasImpliedLaterReferenceDate(currentResult)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return !!nextResult.start.get("day") && !!nextResult.start.get("month") && !!nextResult.start.get("year");
|
|
25
|
+
}
|
|
26
|
+
mergeResults(textBetween, currentResult, nextResult) {
|
|
27
|
+
let timeUnits = constants_1.parseTimeUnits(currentResult.text);
|
|
28
|
+
if (hasImpliedEarlierReferenceDate(currentResult)) {
|
|
29
|
+
timeUnits = timeunits_1.reverseTimeUnits(timeUnits);
|
|
30
|
+
}
|
|
31
|
+
const components = results_1.ParsingComponents.createRelativeFromReference(new results_1.ReferenceWithTimezone(nextResult.start.date()), timeUnits);
|
|
32
|
+
return new results_1.ParsingResult(nextResult.reference, currentResult.index, `${currentResult.text}${textBetween}${nextResult.text}`, components);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.default = ENMergeRelativeDateRefiner;
|
package/dist/timezone.js
CHANGED
package/package.json
CHANGED
package/src/locales/en/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { ParsedResult, ParsingOption } from "../../index";
|
|
|
26
26
|
import { Chrono, Configuration } from "../../chrono";
|
|
27
27
|
import SlashDateFormatParser from "../../common/parsers/SlashDateFormatParser";
|
|
28
28
|
import ENTimeUnitCasualRelativeFormatParser from "./parsers/ENTimeUnitCasualRelativeFormatParser";
|
|
29
|
+
import ENMergeRelativeDateRefiner from "./refiners/ENMergeRelativeDateRefiner";
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Chrono object configured for parsing *casual* English
|
|
@@ -91,7 +92,7 @@ export function createConfiguration(strictMode = true, littleEndian = false): Co
|
|
|
91
92
|
new ENTimeUnitAgoFormatParser(strictMode),
|
|
92
93
|
new ENTimeUnitLaterFormatParser(strictMode),
|
|
93
94
|
],
|
|
94
|
-
refiners: [new ENMergeDateTimeRefiner(), new ENMergeDateRangeRefiner()],
|
|
95
|
+
refiners: [new ENMergeRelativeDateRefiner(), new ENMergeDateTimeRefiner(), new ENMergeDateRangeRefiner()],
|
|
95
96
|
},
|
|
96
97
|
strictMode
|
|
97
98
|
);
|
|
@@ -6,7 +6,7 @@ import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/
|
|
|
6
6
|
import { matchAnyPattern } from "../../../utils/pattern";
|
|
7
7
|
|
|
8
8
|
const PATTERN = new RegExp(
|
|
9
|
-
`(this|
|
|
9
|
+
`(this|last|past|next|after\\s*this)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)` + "(?=\\W|$)",
|
|
10
10
|
"i"
|
|
11
11
|
);
|
|
12
12
|
|
|
@@ -23,7 +23,7 @@ export default class ENRelativeDateFormatParser extends AbstractParserWithWordBo
|
|
|
23
23
|
const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();
|
|
24
24
|
const timeunit = TIME_UNIT_DICTIONARY[unitWord];
|
|
25
25
|
|
|
26
|
-
if (modifier == "next") {
|
|
26
|
+
if (modifier == "next" || modifier.startsWith("after")) {
|
|
27
27
|
const timeUnits = {};
|
|
28
28
|
timeUnits[timeunit] = 1;
|
|
29
29
|
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
@@ -4,7 +4,7 @@ import { ParsingComponents } from "../../../results";
|
|
|
4
4
|
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
|
|
5
5
|
import { reverseTimeUnits } from "../../../utils/timeunits";
|
|
6
6
|
|
|
7
|
-
const PATTERN = new RegExp(`(this|last|past|next|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
|
|
7
|
+
const PATTERN = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
|
|
8
8
|
|
|
9
9
|
export default class ENTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundaryChecking {
|
|
10
10
|
innerPattern(): RegExp {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { MergingRefiner } from "../../../common/abstractRefiners";
|
|
2
|
+
import { ParsingComponents, ParsingResult, ReferenceWithTimezone } from "../../../results";
|
|
3
|
+
import { parseTimeUnits } from "../constants";
|
|
4
|
+
import { reverseTimeUnits } from "../../../utils/timeunits";
|
|
5
|
+
|
|
6
|
+
function hasImpliedEarlierReferenceDate(result: ParsingResult): boolean {
|
|
7
|
+
return result.text.match(/\s+(before|from)$/i) != null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function hasImpliedLaterReferenceDate(result: ParsingResult): boolean {
|
|
11
|
+
return result.text.match(/\s+(after|since)$/i) != null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Merges an absolute date with a relative date.
|
|
16
|
+
* - 2 weeks before 2020-02-13
|
|
17
|
+
* - 2 days after next Friday
|
|
18
|
+
*/
|
|
19
|
+
export default class ENMergeRelativeDateRefiner extends MergingRefiner {
|
|
20
|
+
patternBetween(): RegExp {
|
|
21
|
+
return /^\s*$/i;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
shouldMergeResults(textBetween: string, currentResult: ParsingResult, nextResult: ParsingResult): boolean {
|
|
25
|
+
// Dates need to be next to each other to get merged
|
|
26
|
+
if (!textBetween.match(this.patternBetween())) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check if any relative tokens were swallowed by the first date.
|
|
31
|
+
// E.g. [<relative_date1> from] [<date2>]
|
|
32
|
+
if (!hasImpliedEarlierReferenceDate(currentResult) && !hasImpliedLaterReferenceDate(currentResult)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// make sure that <date2> implies an absolute date
|
|
37
|
+
return !!nextResult.start.get("day") && !!nextResult.start.get("month") && !!nextResult.start.get("year");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
mergeResults(textBetween: string, currentResult: ParsingResult, nextResult: ParsingResult): ParsingResult {
|
|
41
|
+
let timeUnits = parseTimeUnits(currentResult.text);
|
|
42
|
+
if (hasImpliedEarlierReferenceDate(currentResult)) {
|
|
43
|
+
timeUnits = reverseTimeUnits(timeUnits);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const components = ParsingComponents.createRelativeFromReference(
|
|
47
|
+
new ReferenceWithTimezone(nextResult.start.date()),
|
|
48
|
+
timeUnits
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return new ParsingResult(
|
|
52
|
+
nextResult.reference,
|
|
53
|
+
currentResult.index,
|
|
54
|
+
`${currentResult.text}${textBetween}${nextResult.text}`,
|
|
55
|
+
components
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/timezone.ts
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as chrono from "../../src";
|
|
2
|
+
import { testSingleCase } from "../test_util";
|
|
3
|
+
|
|
4
|
+
test("Test - Single Expression", function () {
|
|
5
|
+
testSingleCase(chrono.casual, "2 weeks after yesterday", new Date(2022, 2 - 1, 2), (result) => {
|
|
6
|
+
expect(result.text).toBe("2 weeks after yesterday");
|
|
7
|
+
|
|
8
|
+
expect(result.start).not.toBeNull();
|
|
9
|
+
expect(result.start.get("year")).toBe(2022);
|
|
10
|
+
expect(result.start.get("month")).toBe(2);
|
|
11
|
+
expect(result.start.get("day")).toBe(15);
|
|
12
|
+
expect(result.start.get("weekday")).toBe(2);
|
|
13
|
+
|
|
14
|
+
expect(result.start.isCertain("day")).toBe(false);
|
|
15
|
+
expect(result.start.isCertain("month")).toBe(false);
|
|
16
|
+
expect(result.start.isCertain("year")).toBe(false);
|
|
17
|
+
expect(result.start.isCertain("weekday")).toBe(false);
|
|
18
|
+
|
|
19
|
+
expect(result.start).toBeDate(new Date(2022, 2 - 1, 15, 0));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
testSingleCase(chrono.casual, "2 months before 02/02", new Date(2022, 2 - 1, 2), (result) => {
|
|
23
|
+
expect(result.text).toBe("2 months before 02/02");
|
|
24
|
+
|
|
25
|
+
expect(result.start).not.toBeNull();
|
|
26
|
+
expect(result.start.get("year")).toBe(2021);
|
|
27
|
+
expect(result.start.get("month")).toBe(12);
|
|
28
|
+
expect(result.start.get("day")).toBe(2);
|
|
29
|
+
|
|
30
|
+
expect(result.start.isCertain("day")).toBe(false);
|
|
31
|
+
expect(result.start.isCertain("month")).toBe(true);
|
|
32
|
+
expect(result.start.isCertain("year")).toBe(true);
|
|
33
|
+
|
|
34
|
+
expect(result.start).toBeDate(new Date(2021, 12 - 1, 2, 12));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
testSingleCase(chrono.casual, "2 days after next Friday", new Date(2022, 2 - 1, 2), (result) => {
|
|
38
|
+
expect(result.text).toBe("2 days after next Friday");
|
|
39
|
+
|
|
40
|
+
expect(result.start).not.toBeNull();
|
|
41
|
+
expect(result.start.get("year")).toBe(2022);
|
|
42
|
+
expect(result.start.get("month")).toBe(2);
|
|
43
|
+
expect(result.start.get("day")).toBe(13);
|
|
44
|
+
|
|
45
|
+
expect(result.start.isCertain("day")).toBe(true);
|
|
46
|
+
expect(result.start.isCertain("month")).toBe(true);
|
|
47
|
+
expect(result.start.isCertain("year")).toBe(true);
|
|
48
|
+
|
|
49
|
+
expect(result.start).toBeDate(new Date(2022, 2 - 1, 13, 12));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -135,6 +135,26 @@ test("Test - Future relative expressions", () => {
|
|
|
135
135
|
expect(result.start.isCertain("millisecond")).toBe(false);
|
|
136
136
|
expect(result.start.isCertain("timezoneOffset")).toBe(false);
|
|
137
137
|
});
|
|
138
|
+
|
|
139
|
+
testSingleCase(chrono, "after this year", new Date(2020, 11 - 1, 22, 12, 11, 32, 6), (result, text) => {
|
|
140
|
+
expect(result.text).toBe(text);
|
|
141
|
+
expect(result.start.get("year")).toBe(2021);
|
|
142
|
+
expect(result.start.get("month")).toBe(11);
|
|
143
|
+
expect(result.start.get("day")).toBe(22);
|
|
144
|
+
expect(result.start.get("hour")).toBe(12);
|
|
145
|
+
expect(result.start.get("minute")).toBe(11);
|
|
146
|
+
expect(result.start.get("second")).toBe(32);
|
|
147
|
+
expect(result.start.get("millisecond")).toBe(6);
|
|
148
|
+
|
|
149
|
+
expect(result.start.isCertain("year")).toBe(true);
|
|
150
|
+
expect(result.start.isCertain("month")).toBe(false);
|
|
151
|
+
expect(result.start.isCertain("day")).toBe(false);
|
|
152
|
+
expect(result.start.isCertain("hour")).toBe(false);
|
|
153
|
+
expect(result.start.isCertain("minute")).toBe(false);
|
|
154
|
+
expect(result.start.isCertain("second")).toBe(false);
|
|
155
|
+
expect(result.start.isCertain("millisecond")).toBe(false);
|
|
156
|
+
expect(result.start.isCertain("timezoneOffset")).toBe(false);
|
|
157
|
+
});
|
|
138
158
|
});
|
|
139
159
|
|
|
140
160
|
test("Test - Relative date components' certainty", () => {
|
|
@@ -32,6 +32,22 @@ test("Test - Positive time units", () => {
|
|
|
32
32
|
expect(result.start.get("day")).toBe(18);
|
|
33
33
|
expect(result.start.get("hour")).toBe(12);
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
testSingleCase(chrono, "after a year", new Date(2016, 10 - 1, 1, 12), (result, text) => {
|
|
37
|
+
expect(result.text).toBe(text);
|
|
38
|
+
expect(result.start.get("year")).toBe(2017);
|
|
39
|
+
expect(result.start.get("month")).toBe(10);
|
|
40
|
+
expect(result.start.get("day")).toBe(1);
|
|
41
|
+
expect(result.start.get("hour")).toBe(12);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
testSingleCase(chrono, "after an hour", new Date(2016, 10 - 1, 1, 15), (result, text) => {
|
|
45
|
+
expect(result.text).toBe(text);
|
|
46
|
+
expect(result.start.get("year")).toBe(2016);
|
|
47
|
+
expect(result.start.get("month")).toBe(10);
|
|
48
|
+
expect(result.start.get("day")).toBe(1);
|
|
49
|
+
expect(result.start.get("hour")).toBe(16);
|
|
50
|
+
});
|
|
35
51
|
});
|
|
36
52
|
|
|
37
53
|
test("Test - Negative time units", () => {
|