chrono-node 2.3.0 → 2.3.4
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/benchmark/benchmark.js +4 -0
- package/dist/common/casualReferences.js +3 -1
- package/dist/common/refiners/ForwardDateRefiner.js +1 -1
- package/dist/locales/de/constants.js +1 -1
- package/dist/locales/en/constants.d.ts +1 -1
- package/dist/locales/en/constants.js +5 -5
- package/dist/locales/en/parsers/ENCasualTimeParser.js +1 -1
- package/dist/locales/en/parsers/ENMonthNameLittleEndianParser.js +6 -8
- package/dist/locales/en/parsers/ENRelativeDateFormatParser.js +3 -3
- package/dist/locales/en/parsers/ENTimeUnitAgoFormatParser.js +3 -3
- package/dist/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js +1 -1
- package/dist/locales/en/parsers/ENTimeUnitLaterFormatParser.js +2 -2
- package/dist/locales/en/parsers/ENTimeUnitWithinFormatParser.js +1 -1
- package/dist/locales/fr/constants.js +1 -1
- package/dist/locales/fr/parsers/FRTimeUnitAgoFormatParser.js +1 -1
- package/dist/locales/fr/parsers/FRTimeUnitRelativeFormatParser.js +1 -1
- package/dist/locales/fr/parsers/FRTimeUnitWithinFormatParser.js +1 -1
- package/dist/locales/nl/constants.js +8 -5
- package/dist/locales/nl/index.js +12 -3
- package/dist/locales/nl/parsers/NLRelativeDateFormatParser.d.ts +7 -0
- package/dist/locales/nl/parsers/NLRelativeDateFormatParser.js +57 -0
- package/dist/locales/nl/parsers/NLTimeExpressionParser.d.ts +1 -0
- package/dist/locales/nl/parsers/NLTimeExpressionParser.js +3 -0
- package/dist/locales/nl/parsers/NLTimeUnitAgoFormatParser.d.ts +9 -0
- package/dist/locales/nl/parsers/NLTimeUnitAgoFormatParser.js +23 -0
- package/dist/locales/nl/parsers/NLTimeUnitCasualRelativeFormatParser.d.ts +7 -0
- package/dist/locales/nl/parsers/NLTimeUnitCasualRelativeFormatParser.js +25 -0
- package/dist/locales/nl/parsers/NLTimeUnitLaterFormatParser.d.ts +9 -0
- package/dist/locales/nl/parsers/NLTimeUnitLaterFormatParser.js +22 -0
- package/dist/locales/nl/parsers/NLTimeUnitWithinFormatParser.js +1 -1
- package/dist/results.d.ts +2 -2
- package/dist/results.js +18 -12
- package/dist/timezone.d.ts +1 -1
- package/dist/timezone.js +3 -0
- package/dist/utils/pattern.js +1 -1
- package/package.json +1 -1
- package/src/common/casualReferences.ts +3 -1
- package/src/common/refiners/ForwardDateRefiner.ts +1 -1
- package/src/locales/de/constants.ts +1 -1
- package/src/locales/en/constants.ts +5 -5
- package/src/locales/en/parsers/ENCasualTimeParser.ts +1 -1
- package/src/locales/en/parsers/ENMonthNameLittleEndianParser.ts +9 -10
- package/src/locales/en/parsers/ENRelativeDateFormatParser.ts +3 -3
- package/src/locales/en/parsers/ENTimeUnitAgoFormatParser.ts +3 -4
- package/src/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.ts +1 -1
- package/src/locales/en/parsers/ENTimeUnitLaterFormatParser.ts +2 -2
- package/src/locales/en/parsers/ENTimeUnitWithinFormatParser.ts +1 -1
- package/src/locales/fr/constants.ts +1 -1
- package/src/locales/fr/parsers/FRTimeUnitAgoFormatParser.ts +1 -1
- package/src/locales/fr/parsers/FRTimeUnitRelativeFormatParser.ts +1 -1
- package/src/locales/fr/parsers/FRTimeUnitWithinFormatParser.ts +1 -1
- package/src/locales/nl/constants.ts +11 -5
- package/src/locales/nl/index.ts +12 -3
- package/src/locales/nl/parsers/NLRelativeDateFormatParser.ts +70 -0
- package/src/locales/nl/parsers/NLTimeExpressionParser.ts +4 -0
- package/src/locales/nl/parsers/NLTimeUnitAgoFormatParser.ts +26 -0
- package/src/locales/nl/parsers/NLTimeUnitCasualRelativeFormatParser.ts +27 -0
- package/src/locales/nl/parsers/NLTimeUnitLaterFormatParser.ts +27 -0
- package/src/locales/nl/parsers/NLTimeUnitWithinFormatParser.ts +1 -1
- package/src/results.ts +22 -12
- package/src/timezone.ts +5 -1
- package/src/utils/pattern.ts +1 -1
- package/test/en/en_casual.test.ts +11 -0
- package/test/en/en_month_name_little_endian.test.ts +10 -0
- package/test/en/en_relative.test.ts +8 -0
- package/test/en/en_slash.test.ts +11 -0
- package/test/en/en_timezone_exp.test.ts +85 -10
- package/test/en/en_weekday.test.ts +69 -25
- package/test/en/negative_cases.test.ts +2 -0
- package/test/nl/nl_relative.test.ts +212 -0
- package/test/nl/nl_time_units_ago.test.ts +243 -0
- package/test/nl/nl_time_units_casual_relative.test.ts +130 -0
- package/test/nl/nl_time_units_later.test.ts +250 -0
- package/test/test_util.ts +5 -2
package/dist/timezone.js
CHANGED
package/dist/utils/pattern.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.matchAnyPattern = exports.extractTerms = exports.repeatedTimeunitPattern = void 0;
|
|
4
4
|
function repeatedTimeunitPattern(prefix, singleTimeunitPattern) {
|
|
5
5
|
const singleTimeunitPatternNoCapture = singleTimeunitPattern.replace(/\((?!\?)/g, "(?:");
|
|
6
|
-
return `${prefix}${singleTimeunitPatternNoCapture}\\s
|
|
6
|
+
return `${prefix}${singleTimeunitPatternNoCapture}\\s{0,5}(?:,?\\s{0,5}${singleTimeunitPatternNoCapture}){0,10}`;
|
|
7
7
|
}
|
|
8
8
|
exports.repeatedTimeunitPattern = repeatedTimeunitPattern;
|
|
9
9
|
function extractTerms(dictionary) {
|
package/package.json
CHANGED
|
@@ -8,7 +8,9 @@ export function now(reference: ReferenceWithTimezone): ParsingComponents {
|
|
|
8
8
|
const component = new ParsingComponents(reference, {});
|
|
9
9
|
assignSimilarDate(component, targetDate);
|
|
10
10
|
assignSimilarTime(component, targetDate);
|
|
11
|
-
|
|
11
|
+
if (reference.timezoneOffset !== null) {
|
|
12
|
+
component.assign("timezoneOffset", targetDate.utcOffset());
|
|
13
|
+
}
|
|
12
14
|
return component;
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -34,7 +34,7 @@ export default class ForwardDateRefiner implements Refiner {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
if (result.start.isOnlyWeekdayComponent() && refMoment.isAfter(result.start.dayjs())) {
|
|
37
|
-
if (refMoment.day()
|
|
37
|
+
if (refMoment.day() >= result.start.get("weekday")) {
|
|
38
38
|
refMoment = refMoment.day(result.start.get("weekday") + 7);
|
|
39
39
|
} else {
|
|
40
40
|
refMoment = refMoment.day(<number>result.start.get("weekday"));
|
|
@@ -108,7 +108,7 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
|
|
|
108
108
|
|
|
109
109
|
export const NUMBER_PATTERN = `(?:${matchAnyPattern(
|
|
110
110
|
INTEGER_WORD_DICTIONARY
|
|
111
|
-
)}|[0-9]+|[0-9]+\\.[0-9]+|half(?:\\s*an?)?|an
|
|
111
|
+
)}|[0-9]+|[0-9]+\\.[0-9]+|half(?:\\s*an?)?|an?\\b(?:\\s*few)?|few|several|a?\\s*couple\\s*(?:of)?)`;
|
|
112
112
|
|
|
113
113
|
export function parseNumberPattern(match: string): number {
|
|
114
114
|
const num = match.toLowerCase();
|
|
@@ -162,7 +162,7 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
|
|
|
162
162
|
|
|
163
163
|
export const NUMBER_PATTERN = `(?:${matchAnyPattern(
|
|
164
164
|
INTEGER_WORD_DICTIONARY
|
|
165
|
-
)}|[0-9]+|[0-9]+\\.[0-9]+|half(?:\\s
|
|
165
|
+
)}|[0-9]+|[0-9]+\\.[0-9]+|half(?:\\s{0,2}an?)?|an?\\b(?:\\s{0,2}few)?|few|several|a?\\s{0,2}couple\\s{0,2}(?:of)?)`;
|
|
166
166
|
|
|
167
167
|
export function parseNumberPattern(match: string): number {
|
|
168
168
|
const num = match.toLowerCase();
|
|
@@ -198,7 +198,7 @@ export function parseOrdinalNumberPattern(match: string): number {
|
|
|
198
198
|
|
|
199
199
|
//-----------------------------
|
|
200
200
|
|
|
201
|
-
export const YEAR_PATTERN = `(?:[1-9][0-9]{0,3}\\s
|
|
201
|
+
export const YEAR_PATTERN = `(?:[1-9][0-9]{0,3}\\s{0,2}(?:BE|AD|BC|BCE|CE)|[1-2][0-9]{3}|[5-9][0-9])`;
|
|
202
202
|
export function parseYear(match: string): number {
|
|
203
203
|
if (/BE/i.test(match)) {
|
|
204
204
|
// Buddhist Era
|
|
@@ -224,10 +224,10 @@ export function parseYear(match: string): number {
|
|
|
224
224
|
|
|
225
225
|
//-----------------------------
|
|
226
226
|
|
|
227
|
-
const SINGLE_TIME_UNIT_PATTERN = `(${NUMBER_PATTERN})\\s{0,
|
|
227
|
+
const SINGLE_TIME_UNIT_PATTERN = `(${NUMBER_PATTERN})\\s{0,3}(${matchAnyPattern(TIME_UNIT_DICTIONARY)})`;
|
|
228
228
|
const SINGLE_TIME_UNIT_REGEX = new RegExp(SINGLE_TIME_UNIT_PATTERN, "i");
|
|
229
229
|
|
|
230
|
-
export const TIME_UNITS_PATTERN = repeatedTimeunitPattern(`(?:(?:about|around)\\s
|
|
230
|
+
export const TIME_UNITS_PATTERN = repeatedTimeunitPattern(`(?:(?:about|around)\\s{0,3})?`, SINGLE_TIME_UNIT_PATTERN);
|
|
231
231
|
|
|
232
232
|
export function parseTimeUnits(timeunitText): TimeUnits {
|
|
233
233
|
const fragments = {};
|
|
@@ -235,7 +235,7 @@ export function parseTimeUnits(timeunitText): TimeUnits {
|
|
|
235
235
|
let match = SINGLE_TIME_UNIT_REGEX.exec(remainingText);
|
|
236
236
|
while (match) {
|
|
237
237
|
collectDateTimeFragment(fragments, match);
|
|
238
|
-
remainingText = remainingText.substring(match[0].length);
|
|
238
|
+
remainingText = remainingText.substring(match[0].length).trim();
|
|
239
239
|
match = SINGLE_TIME_UNIT_REGEX.exec(remainingText);
|
|
240
240
|
}
|
|
241
241
|
return fragments;
|
|
@@ -4,7 +4,7 @@ import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/
|
|
|
4
4
|
import dayjs from "dayjs";
|
|
5
5
|
import { assignTheNextDay } from "../../../utils/dayjs";
|
|
6
6
|
|
|
7
|
-
const PATTERN = /(?:this)?\s
|
|
7
|
+
const PATTERN = /(?:this)?\s{0,3}(morning|afternoon|evening|night|midnight|noon)(?=\W|$)/i;
|
|
8
8
|
|
|
9
9
|
export default class ENCasualTimeParser extends AbstractParserWithWordBoundaryChecking {
|
|
10
10
|
innerPattern() {
|
|
@@ -7,20 +7,19 @@ import { ORDINAL_NUMBER_PATTERN, parseOrdinalNumberPattern } from "../constants"
|
|
|
7
7
|
import { matchAnyPattern } from "../../../utils/pattern";
|
|
8
8
|
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
|
|
9
9
|
|
|
10
|
+
// prettier-ignore
|
|
10
11
|
const PATTERN = new RegExp(
|
|
11
|
-
|
|
12
|
-
`(${ORDINAL_NUMBER_PATTERN})` +
|
|
13
|
-
"(?:\\s*" +
|
|
14
|
-
"(?:to|\\-|\\–|until|through|till|\\s)\\s*" +
|
|
12
|
+
`(?:on\\s{0,3})?` +
|
|
15
13
|
`(${ORDINAL_NUMBER_PATTERN})` +
|
|
14
|
+
`(?:` +
|
|
15
|
+
`\\s{0,3}(?:to|\\-|\\–|until|through|till)?\\s{0,3}` +
|
|
16
|
+
`(${ORDINAL_NUMBER_PATTERN})` +
|
|
16
17
|
")?" +
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
matchAnyPattern(MONTH_DICTIONARY) +
|
|
20
|
-
")" +
|
|
18
|
+
`(?:-|/|\\s{0,3}(?:of)?\\s{0,3})` +
|
|
19
|
+
`(${matchAnyPattern(MONTH_DICTIONARY)})` +
|
|
21
20
|
"(?:" +
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
`(?:-|/|,?\\s{0,3})` +
|
|
22
|
+
`(${YEAR_PATTERN}(?![^\\s]\\d))` +
|
|
24
23
|
")?" +
|
|
25
24
|
"(?=\\W|$)",
|
|
26
25
|
"i"
|
|
@@ -26,17 +26,17 @@ export default class ENRelativeDateFormatParser extends AbstractParserWithWordBo
|
|
|
26
26
|
if (modifier == "next") {
|
|
27
27
|
const timeUnits = {};
|
|
28
28
|
timeUnits[timeunit] = 1;
|
|
29
|
-
return ParsingComponents.
|
|
29
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (modifier == "last" || modifier == "past") {
|
|
33
33
|
const timeUnits = {};
|
|
34
34
|
timeUnits[timeunit] = -1;
|
|
35
|
-
return ParsingComponents.
|
|
35
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const components = context.createParsingComponents();
|
|
39
|
-
let date = dayjs(context.
|
|
39
|
+
let date = dayjs(context.reference.instant);
|
|
40
40
|
|
|
41
41
|
// This week
|
|
42
42
|
if (unitWord.match(/week/i)) {
|
|
@@ -4,9 +4,8 @@ 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(
|
|
8
|
-
|
|
9
|
-
const STRICT_PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "ago(?=(?:\\W|$))", "i");
|
|
7
|
+
const PATTERN = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=(?:\\W|$))`, "i");
|
|
8
|
+
const STRICT_PATTERN = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}ago(?=(?:\\W|$))`, "i");
|
|
10
9
|
|
|
11
10
|
export default class ENTimeUnitAgoFormatParser extends AbstractParserWithWordBoundaryChecking {
|
|
12
11
|
constructor(private strictMode: boolean) {
|
|
@@ -21,6 +20,6 @@ export default class ENTimeUnitAgoFormatParser extends AbstractParserWithWordBou
|
|
|
21
20
|
const timeUnits = parseTimeUnits(match[1]);
|
|
22
21
|
const outputTimeUnits = reverseTimeUnits(timeUnits);
|
|
23
22
|
|
|
24
|
-
return ParsingComponents.
|
|
23
|
+
return ParsingComponents.createRelativeFromReference(context.reference, outputTimeUnits);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
@@ -22,6 +22,6 @@ export default class ENTimeUnitCasualRelativeFormatParser extends AbstractParser
|
|
|
22
22
|
break;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
return ParsingComponents.
|
|
25
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -4,7 +4,7 @@ import { ParsingComponents } from "../../../results";
|
|
|
4
4
|
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
|
|
5
5
|
|
|
6
6
|
const PATTERN = new RegExp(
|
|
7
|
-
|
|
7
|
+
`(${TIME_UNITS_PATTERN})\\s{0,5}(?:later|after|from now|henceforth|forward|out)` + "(?=(?:\\W|$))",
|
|
8
8
|
"i"
|
|
9
9
|
);
|
|
10
10
|
|
|
@@ -22,6 +22,6 @@ export default class ENTimeUnitLaterFormatParser extends AbstractParserWithWordB
|
|
|
22
22
|
|
|
23
23
|
innerExtract(context: ParsingContext, match: RegExpMatchArray) {
|
|
24
24
|
const fragments = parseTimeUnits(match[GROUP_NUM_TIMEUNITS]);
|
|
25
|
-
return ParsingComponents.
|
|
25
|
+
return ParsingComponents.createRelativeFromReference(context.reference, fragments);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -21,6 +21,6 @@ export default class ENTimeUnitWithinFormatParser extends AbstractParserWithWord
|
|
|
21
21
|
|
|
22
22
|
innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
|
|
23
23
|
const timeUnits = parseTimeUnits(match[1]);
|
|
24
|
-
return ParsingComponents.
|
|
24
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -105,7 +105,7 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType | QUnitType } =
|
|
|
105
105
|
|
|
106
106
|
export const NUMBER_PATTERN = `(?:${matchAnyPattern(
|
|
107
107
|
INTEGER_WORD_DICTIONARY
|
|
108
|
-
)}|[0-9]+|[0-9]+\\.[0-9]+|une
|
|
108
|
+
)}|[0-9]+|[0-9]+\\.[0-9]+|une?\\b|quelques?|demi-?)`;
|
|
109
109
|
|
|
110
110
|
export function parseNumberPattern(match: string): number {
|
|
111
111
|
const num = match.toLowerCase();
|
|
@@ -17,6 +17,6 @@ export default class FRTimeUnitAgoFormatParser extends AbstractParserWithWordBou
|
|
|
17
17
|
const timeUnits = parseTimeUnits(match[1]);
|
|
18
18
|
const outputTimeUnits = reverseTimeUnits(timeUnits);
|
|
19
19
|
|
|
20
|
-
return ParsingComponents.
|
|
20
|
+
return ParsingComponents.createRelativeFromReference(context.reference, outputTimeUnits);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -38,6 +38,6 @@ export default class FRTimeUnitAgoFormatParser extends AbstractParserWithWordBou
|
|
|
38
38
|
timeUnits = reverseTimeUnits(timeUnits);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
return ParsingComponents.
|
|
41
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -10,6 +10,6 @@ export default class FRTimeUnitWithinFormatParser extends AbstractParserWithWord
|
|
|
10
10
|
|
|
11
11
|
innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
|
|
12
12
|
const timeUnits = parseTimeUnits(match[1]);
|
|
13
|
-
return ParsingComponents.
|
|
13
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -141,11 +141,14 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
|
|
|
141
141
|
min: "minute",
|
|
142
142
|
mins: "minute",
|
|
143
143
|
minute: "minute",
|
|
144
|
+
minuut: "minute",
|
|
144
145
|
minuten: "minute",
|
|
146
|
+
minuutje: "minute",
|
|
145
147
|
h: "hour",
|
|
146
148
|
hr: "hour",
|
|
147
149
|
hrs: "hour",
|
|
148
150
|
uur: "hour",
|
|
151
|
+
u: "hour",
|
|
149
152
|
uren: "hour",
|
|
150
153
|
dag: "d",
|
|
151
154
|
dagen: "d",
|
|
@@ -160,18 +163,21 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
|
|
|
160
163
|
|
|
161
164
|
//-----------------------------
|
|
162
165
|
|
|
163
|
-
export const NUMBER_PATTERN = `(?:${matchAnyPattern(
|
|
166
|
+
export const NUMBER_PATTERN = `(?:${matchAnyPattern(
|
|
167
|
+
INTEGER_WORD_DICTIONARY
|
|
168
|
+
)}|[0-9]+|[0-9]+[\\.,][0-9]+|halve?|half|paar)`;
|
|
164
169
|
|
|
165
170
|
export function parseNumberPattern(match: string): number {
|
|
166
171
|
const num = match.toLowerCase();
|
|
167
172
|
if (INTEGER_WORD_DICTIONARY[num] !== undefined) {
|
|
168
173
|
return INTEGER_WORD_DICTIONARY[num];
|
|
169
|
-
} else if (num === "
|
|
170
|
-
return
|
|
171
|
-
} else if (num.match(/halve?/)) {
|
|
174
|
+
} else if (num === "paar") {
|
|
175
|
+
return 2;
|
|
176
|
+
} else if (num === "half" || num.match(/halve?/)) {
|
|
172
177
|
return 0.5;
|
|
173
178
|
}
|
|
174
|
-
|
|
179
|
+
// Replace "," with "." to support some European languages
|
|
180
|
+
return parseFloat(num.replace(",", "."));
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
//-----------------------------
|
package/src/locales/nl/index.ts
CHANGED
|
@@ -20,6 +20,10 @@ import NLSlashMonthFormatParser from "./parsers/NLSlashMonthFormatParser";
|
|
|
20
20
|
import NLTimeExpressionParser from "./parsers/NLTimeExpressionParser";
|
|
21
21
|
import NLCasualYearMonthDayParser from "./parsers/NLCasualYearMonthDayParser";
|
|
22
22
|
import NLCasualDateTimeParser from "./parsers/NLCasualDateTimeParser";
|
|
23
|
+
import NLTimeUnitCasualRelativeFormatParser from "./parsers/NLTimeUnitCasualRelativeFormatParser";
|
|
24
|
+
import NLRelativeDateFormatParser from "./parsers/NLRelativeDateFormatParser";
|
|
25
|
+
import NLTimeUnitAgoFormatParser from "./parsers/NLTimeUnitAgoFormatParser";
|
|
26
|
+
import NLTimeUnitLaterFormatParser from "./parsers/NLTimeUnitLaterFormatParser";
|
|
23
27
|
|
|
24
28
|
// Shortcuts
|
|
25
29
|
export const casual = new Chrono(createCasualConfiguration());
|
|
@@ -38,6 +42,9 @@ export function createCasualConfiguration(littleEndian = true): Configuration {
|
|
|
38
42
|
option.parsers.unshift(new NLCasualDateParser());
|
|
39
43
|
option.parsers.unshift(new NLCasualTimeParser());
|
|
40
44
|
option.parsers.unshift(new NLCasualDateTimeParser());
|
|
45
|
+
option.parsers.unshift(new NLMonthNameParser());
|
|
46
|
+
option.parsers.unshift(new NLRelativeDateFormatParser());
|
|
47
|
+
option.parsers.unshift(new NLTimeUnitCasualRelativeFormatParser());
|
|
41
48
|
return option;
|
|
42
49
|
}
|
|
43
50
|
|
|
@@ -49,13 +56,15 @@ export function createConfiguration(strictMode = true, littleEndian = true): Con
|
|
|
49
56
|
{
|
|
50
57
|
parsers: [
|
|
51
58
|
new SlashDateFormatParser(littleEndian),
|
|
59
|
+
new NLTimeUnitWithinFormatParser(),
|
|
52
60
|
new NLMonthNameMiddleEndianParser(),
|
|
53
61
|
new NLMonthNameParser(),
|
|
54
|
-
new NLTimeExpressionParser(),
|
|
55
|
-
new NLTimeUnitWithinFormatParser(),
|
|
56
|
-
new NLSlashMonthFormatParser(),
|
|
57
62
|
new NLWeekdayParser(),
|
|
58
63
|
new NLCasualYearMonthDayParser(),
|
|
64
|
+
new NLSlashMonthFormatParser(),
|
|
65
|
+
new NLTimeExpressionParser(strictMode),
|
|
66
|
+
new NLTimeUnitAgoFormatParser(strictMode),
|
|
67
|
+
new NLTimeUnitLaterFormatParser(strictMode),
|
|
59
68
|
],
|
|
60
69
|
refiners: [new NLMergeDateTimeRefiner(), new NLMergeDateRangeRefiner()],
|
|
61
70
|
},
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { TIME_UNIT_DICTIONARY } from "../constants";
|
|
2
|
+
import { ParsingContext } from "../../../chrono";
|
|
3
|
+
import { ParsingComponents } from "../../../results";
|
|
4
|
+
import dayjs from "dayjs";
|
|
5
|
+
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
|
|
6
|
+
import { matchAnyPattern } from "../../../utils/pattern";
|
|
7
|
+
|
|
8
|
+
const PATTERN = new RegExp(
|
|
9
|
+
`(dit|deze|komende|volgend|volgende|afgelopen|vorige)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)` +
|
|
10
|
+
"(?=\\W|$)",
|
|
11
|
+
"i"
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const MODIFIER_WORD_GROUP = 1;
|
|
15
|
+
const RELATIVE_WORD_GROUP = 2;
|
|
16
|
+
|
|
17
|
+
export default class NLRelativeDateFormatParser extends AbstractParserWithWordBoundaryChecking {
|
|
18
|
+
innerPattern(): RegExp {
|
|
19
|
+
return PATTERN;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
|
|
23
|
+
const modifier = match[MODIFIER_WORD_GROUP].toLowerCase();
|
|
24
|
+
const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();
|
|
25
|
+
const timeunit = TIME_UNIT_DICTIONARY[unitWord];
|
|
26
|
+
|
|
27
|
+
if (modifier == "volgend" || modifier == "volgende" || modifier == "komende") {
|
|
28
|
+
const timeUnits = {};
|
|
29
|
+
timeUnits[timeunit] = 1;
|
|
30
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (modifier == "afgelopen" || modifier == "vorige") {
|
|
34
|
+
const timeUnits = {};
|
|
35
|
+
timeUnits[timeunit] = -1;
|
|
36
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const components = context.createParsingComponents();
|
|
40
|
+
let date = dayjs(context.reference.instant);
|
|
41
|
+
|
|
42
|
+
// This week
|
|
43
|
+
if (unitWord.match(/week/i)) {
|
|
44
|
+
date = date.add(-date.get("d"), "d");
|
|
45
|
+
components.imply("day", date.date());
|
|
46
|
+
components.imply("month", date.month() + 1);
|
|
47
|
+
components.imply("year", date.year());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// This month
|
|
51
|
+
else if (unitWord.match(/maand/i)) {
|
|
52
|
+
date = date.add(-date.date() + 1, "d");
|
|
53
|
+
components.imply("day", date.date());
|
|
54
|
+
components.assign("year", date.year());
|
|
55
|
+
components.assign("month", date.month() + 1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// This year
|
|
59
|
+
else if (unitWord.match(/jaar/i)) {
|
|
60
|
+
date = date.add(-date.date() + 1, "d");
|
|
61
|
+
date = date.add(-date.month(), "month");
|
|
62
|
+
|
|
63
|
+
components.imply("day", date.date());
|
|
64
|
+
components.imply("month", date.month() + 1);
|
|
65
|
+
components.assign("year", date.year());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return components;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -11,6 +11,10 @@ export default class NLTimeExpressionParser extends AbstractTimeExpressionParser
|
|
|
11
11
|
return "\\s*(?:\\-|\\–|\\~|\\〜|om|\\?)\\s*";
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
primarySuffix(): string {
|
|
15
|
+
return "(?:\\s*(?:uur))?(?!/)(?=\\W|$)";
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
extractPrimaryTimeComponents(context: ParsingContext, match: RegExpMatchArray): ParsingComponents | null {
|
|
15
19
|
// This looks more like a year e.g. 2020
|
|
16
20
|
if (match[0].match(/^\s*\d{4}\s*$/)) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ParsingContext } from "../../../chrono";
|
|
2
|
+
import { parseTimeUnits, TIME_UNITS_PATTERN } from "../constants";
|
|
3
|
+
import { ParsingComponents } from "../../../results";
|
|
4
|
+
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
|
|
5
|
+
import { reverseTimeUnits } from "../../../utils/timeunits";
|
|
6
|
+
|
|
7
|
+
const PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "(?:geleden|voor|eerder)(?=(?:\\W|$))", "i");
|
|
8
|
+
|
|
9
|
+
const STRICT_PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "geleden(?=(?:\\W|$))", "i");
|
|
10
|
+
|
|
11
|
+
export default class NLTimeUnitAgoFormatParser extends AbstractParserWithWordBoundaryChecking {
|
|
12
|
+
constructor(private strictMode: boolean) {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
innerPattern(): RegExp {
|
|
17
|
+
return this.strictMode ? STRICT_PATTERN : PATTERN;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
innerExtract(context: ParsingContext, match: RegExpMatchArray) {
|
|
21
|
+
const timeUnits = parseTimeUnits(match[1]);
|
|
22
|
+
const outputTimeUnits = reverseTimeUnits(timeUnits);
|
|
23
|
+
|
|
24
|
+
return ParsingComponents.createRelativeFromReference(context.reference, outputTimeUnits);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { TIME_UNITS_PATTERN, parseTimeUnits } from "../constants";
|
|
2
|
+
import { ParsingContext } from "../../../chrono";
|
|
3
|
+
import { ParsingComponents } from "../../../results";
|
|
4
|
+
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
|
|
5
|
+
import { reverseTimeUnits } from "../../../utils/timeunits";
|
|
6
|
+
|
|
7
|
+
const PATTERN = new RegExp(`(deze|vorige|afgelopen|komende|over|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
|
|
8
|
+
|
|
9
|
+
export default class NLTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundaryChecking {
|
|
10
|
+
innerPattern(): RegExp {
|
|
11
|
+
return PATTERN;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
|
|
15
|
+
const prefix = match[1].toLowerCase();
|
|
16
|
+
let timeUnits = parseTimeUnits(match[2]);
|
|
17
|
+
switch (prefix) {
|
|
18
|
+
case "vorige":
|
|
19
|
+
case "afgelopen":
|
|
20
|
+
case "-":
|
|
21
|
+
timeUnits = reverseTimeUnits(timeUnits);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ParsingContext } from "../../../chrono";
|
|
2
|
+
import { parseTimeUnits, TIME_UNITS_PATTERN } from "../constants";
|
|
3
|
+
import { ParsingComponents } from "../../../results";
|
|
4
|
+
import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
|
|
5
|
+
|
|
6
|
+
const PATTERN = new RegExp(
|
|
7
|
+
"" + "(" + TIME_UNITS_PATTERN + ")" + "(later|na|vanaf nu|voortaan|vooruit|uit)" + "(?=(?:\\W|$))",
|
|
8
|
+
"i"
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const STRICT_PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "(later|vanaf nu)" + "(?=(?:\\W|$))", "i");
|
|
12
|
+
const GROUP_NUM_TIMEUNITS = 1;
|
|
13
|
+
|
|
14
|
+
export default class NLTimeUnitLaterFormatParser extends AbstractParserWithWordBoundaryChecking {
|
|
15
|
+
constructor(private strictMode: boolean) {
|
|
16
|
+
super();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
innerPattern(): RegExp {
|
|
20
|
+
return this.strictMode ? STRICT_PATTERN : PATTERN;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
innerExtract(context: ParsingContext, match: RegExpMatchArray) {
|
|
24
|
+
const fragments = parseTimeUnits(match[GROUP_NUM_TIMEUNITS]);
|
|
25
|
+
return ParsingComponents.createRelativeFromReference(context.reference, fragments);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -10,6 +10,6 @@ export default class NLTimeUnitWithinFormatParser extends AbstractParserWithWord
|
|
|
10
10
|
|
|
11
11
|
innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
|
|
12
12
|
const timeUnits = parseTimeUnits(match[1]);
|
|
13
|
-
return ParsingComponents.
|
|
13
|
+
return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
|
|
14
14
|
}
|
|
15
15
|
}
|
package/src/results.ts
CHANGED
|
@@ -8,7 +8,7 @@ dayjs.extend(quarterOfYear);
|
|
|
8
8
|
|
|
9
9
|
export class ReferenceWithTimezone {
|
|
10
10
|
readonly instant: Date;
|
|
11
|
-
readonly timezoneOffset
|
|
11
|
+
readonly timezoneOffset?: number;
|
|
12
12
|
|
|
13
13
|
constructor(input?: ParsingReference | Date) {
|
|
14
14
|
input = input ?? new Date();
|
|
@@ -17,7 +17,7 @@ export class ReferenceWithTimezone {
|
|
|
17
17
|
this.timezoneOffset = -input.getTimezoneOffset();
|
|
18
18
|
} else {
|
|
19
19
|
this.instant = input.instant ?? new Date();
|
|
20
|
-
this.timezoneOffset = toTimezoneOffset(input.timezone
|
|
20
|
+
this.timezoneOffset = toTimezoneOffset(input.timezone);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -142,7 +142,7 @@ export class ParsingComponents implements ParsedComponents {
|
|
|
142
142
|
|
|
143
143
|
date(): Date {
|
|
144
144
|
const date = this.dateWithoutTimezoneAdjustment();
|
|
145
|
-
return new Date(date.getTime() + this.getSystemTimezoneAdjustmentMinute() * 60000);
|
|
145
|
+
return new Date(date.getTime() + this.getSystemTimezoneAdjustmentMinute(date) * 60000);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
private dateWithoutTimezoneAdjustment() {
|
|
@@ -160,31 +160,41 @@ export class ParsingComponents implements ParsedComponents {
|
|
|
160
160
|
return date;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
private getSystemTimezoneAdjustmentMinute() {
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
private getSystemTimezoneAdjustmentMinute(date?: Date) {
|
|
164
|
+
if (!date || date.getTime() < 0) {
|
|
165
|
+
// Javascript date timezone calculation got effect when the time epoch < 0
|
|
166
|
+
// e.g. new Date('Tue Feb 02 1300 00:00:00 GMT+0900 (JST)') => Tue Feb 02 1300 00:18:59 GMT+0918 (JST)
|
|
167
|
+
date = new Date();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const currentTimezoneOffset = -date.getTimezoneOffset();
|
|
171
|
+
const targetTimezoneOffset =
|
|
172
|
+
this.get("timezoneOffset") ?? this.reference.timezoneOffset ?? currentTimezoneOffset;
|
|
166
173
|
|
|
167
174
|
return currentTimezoneOffset - targetTimezoneOffset;
|
|
168
175
|
}
|
|
169
176
|
|
|
170
|
-
static
|
|
171
|
-
|
|
177
|
+
static createRelativeFromReference(
|
|
178
|
+
reference: ReferenceWithTimezone,
|
|
172
179
|
fragments: { [c in OpUnitType | QUnitType]?: number }
|
|
173
180
|
): ParsingComponents {
|
|
174
|
-
let date = dayjs(
|
|
181
|
+
let date = dayjs(reference.instant);
|
|
175
182
|
for (const key in fragments) {
|
|
176
183
|
date = date.add(fragments[key as OpUnitType], key as OpUnitType);
|
|
177
184
|
}
|
|
178
185
|
|
|
179
|
-
const reference = new ReferenceWithTimezone(refInstant);
|
|
180
186
|
const components = new ParsingComponents(reference);
|
|
181
187
|
if (fragments["hour"] || fragments["minute"] || fragments["second"]) {
|
|
182
188
|
assignSimilarTime(components, date);
|
|
183
189
|
assignSimilarDate(components, date);
|
|
184
|
-
|
|
190
|
+
if (reference.timezoneOffset !== null) {
|
|
191
|
+
components.assign("timezoneOffset", -reference.instant.getTimezoneOffset());
|
|
192
|
+
}
|
|
185
193
|
} else {
|
|
186
194
|
implySimilarTime(components, date);
|
|
187
|
-
|
|
195
|
+
if (reference.timezoneOffset !== null) {
|
|
196
|
+
components.imply("timezoneOffset", -reference.instant.getTimezoneOffset());
|
|
197
|
+
}
|
|
188
198
|
|
|
189
199
|
if (fragments["d"]) {
|
|
190
200
|
components.assign("day", date.date());
|
package/src/timezone.ts
CHANGED
|
@@ -192,7 +192,11 @@ export const TIMEZONE_ABBR_MAP = {
|
|
|
192
192
|
YEKT: 360,
|
|
193
193
|
};
|
|
194
194
|
|
|
195
|
-
export function toTimezoneOffset(timezoneInput: string | number): number {
|
|
195
|
+
export function toTimezoneOffset(timezoneInput: string | number): number | null {
|
|
196
|
+
if (timezoneInput === null) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
196
200
|
if (typeof timezoneInput === "number") {
|
|
197
201
|
return timezoneInput;
|
|
198
202
|
}
|
package/src/utils/pattern.ts
CHANGED
|
@@ -2,7 +2,7 @@ type DictionaryLike = string[] | { [word: string]: unknown } | Map<string, unkno
|
|
|
2
2
|
|
|
3
3
|
export function repeatedTimeunitPattern(prefix: string, singleTimeunitPattern: string): string {
|
|
4
4
|
const singleTimeunitPatternNoCapture = singleTimeunitPattern.replace(/\((?!\?)/g, "(?:");
|
|
5
|
-
return `${prefix}${singleTimeunitPatternNoCapture}\\s
|
|
5
|
+
return `${prefix}${singleTimeunitPatternNoCapture}\\s{0,5}(?:,?\\s{0,5}${singleTimeunitPatternNoCapture}){0,10}`;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function extractTerms(dictionary: DictionaryLike): string[] {
|
|
@@ -21,6 +21,17 @@ test("Test - Single Expression", () => {
|
|
|
21
21
|
expect(result.start).toBeDate(new Date(2012, 7, 10, 8, 9, 10, 11));
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
+
testSingleCase(
|
|
25
|
+
chrono.casual,
|
|
26
|
+
"The Deadline is now, without implicit local timezone",
|
|
27
|
+
{ instant: new Date(1637674343000), timezone: null },
|
|
28
|
+
(result) => {
|
|
29
|
+
expect(result.text).toBe("now");
|
|
30
|
+
expect(result.start).toBeDate(new Date(1637674343000));
|
|
31
|
+
expect(result.start.isCertain("timezoneOffset")).toBe(false);
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
24
35
|
testSingleCase(chrono.casual, "The Deadline is today", new Date(2012, 7, 10, 14, 12), (result) => {
|
|
25
36
|
expect(result.index).toBe(16);
|
|
26
37
|
expect(result.text).toBe("today");
|