chrono-node 2.3.5 → 2.3.8

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.
Files changed (95) hide show
  1. package/README.md +1 -1
  2. package/dist/common/refiners/ExtractTimezoneAbbrRefiner.js +6 -1
  3. package/dist/locales/de/constants.d.ts +3 -3
  4. package/dist/locales/de/constants.js +38 -20
  5. package/dist/locales/de/index.js +4 -0
  6. package/dist/locales/de/parsers/DESpecificTimeExpressionParser.d.ts +7 -0
  7. package/dist/locales/de/parsers/DESpecificTimeExpressionParser.js +114 -0
  8. package/dist/locales/de/parsers/DETimeExpressionParser.d.ts +0 -1
  9. package/dist/locales/de/parsers/DETimeExpressionParser.js +3 -21
  10. package/dist/locales/de/parsers/DETimeUnitRelativeFormatParser.d.ts +8 -0
  11. package/dist/locales/de/parsers/DETimeUnitRelativeFormatParser.js +34 -0
  12. package/dist/locales/en/constants.d.ts +2 -2
  13. package/dist/locales/en/constants.js +3 -0
  14. package/dist/locales/en/index.js +2 -1
  15. package/dist/locales/en/parsers/ENRelativeDateFormatParser.js +2 -2
  16. package/dist/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js +1 -1
  17. package/dist/locales/en/refiners/ENMergeRelativeDateRefiner.d.ts +7 -0
  18. package/dist/locales/en/refiners/ENMergeRelativeDateRefiner.js +35 -0
  19. package/dist/locales/zh/hans/constants.d.ts +27 -0
  20. package/dist/locales/zh/hans/constants.js +51 -0
  21. package/dist/locales/zh/hans/index.d.ts +9 -0
  22. package/dist/locales/zh/hans/index.js +49 -0
  23. package/dist/locales/zh/hans/parsers/ZHHansCasualDateParser.d.ts +7 -0
  24. package/dist/locales/zh/hans/parsers/ZHHansCasualDateParser.js +138 -0
  25. package/dist/locales/zh/hans/parsers/ZHHansDateParser.d.ts +6 -0
  26. package/dist/locales/zh/hans/parsers/ZHHansDateParser.js +72 -0
  27. package/dist/locales/zh/hans/parsers/ZHHansDeadlineFormatParser.d.ts +6 -0
  28. package/dist/locales/zh/hans/parsers/ZHHansDeadlineFormatParser.js +78 -0
  29. package/dist/locales/zh/hans/parsers/ZHHansRelationWeekdayParser.d.ts +7 -0
  30. package/dist/locales/zh/hans/parsers/ZHHansRelationWeekdayParser.js +70 -0
  31. package/dist/locales/zh/hans/parsers/ZHHansTimeExpressionParser.d.ts +6 -0
  32. package/dist/locales/zh/hans/parsers/ZHHansTimeExpressionParser.js +438 -0
  33. package/dist/locales/zh/hans/parsers/ZHHansWeekdayParser.d.ts +7 -0
  34. package/dist/locales/zh/hans/parsers/ZHHansWeekdayParser.js +46 -0
  35. package/dist/locales/zh/hans/refiners/ZHHansMergeDateRangeRefiner.d.ts +4 -0
  36. package/dist/locales/zh/hans/refiners/ZHHansMergeDateRangeRefiner.js +12 -0
  37. package/dist/locales/zh/hans/refiners/ZHHansMergeDateTimeRefiner.d.ts +4 -0
  38. package/dist/locales/zh/hans/refiners/ZHHansMergeDateTimeRefiner.js +12 -0
  39. package/dist/locales/zh/hant/index.d.ts +9 -0
  40. package/dist/locales/zh/hant/index.js +49 -0
  41. package/dist/locales/zh/index.d.ts +2 -9
  42. package/dist/locales/zh/index.js +13 -46
  43. package/dist/results.d.ts +1 -1
  44. package/dist/results.js +1 -2
  45. package/dist/timezone.d.ts +1 -1
  46. package/dist/timezone.js +3 -3
  47. package/package.json +1 -1
  48. package/src/common/refiners/ExtractTimezoneAbbrRefiner.ts +11 -1
  49. package/src/locales/de/constants.ts +42 -22
  50. package/src/locales/de/index.ts +4 -0
  51. package/src/locales/de/parsers/DESpecificTimeExpressionParser.ts +150 -0
  52. package/src/locales/de/parsers/DETimeExpressionParser.ts +4 -23
  53. package/src/locales/de/parsers/DETimeUnitRelativeFormatParser.ts +42 -0
  54. package/src/locales/en/constants.ts +5 -2
  55. package/src/locales/en/index.ts +2 -1
  56. package/src/locales/en/parsers/ENMonthNameLittleEndianParser.ts +0 -2
  57. package/src/locales/en/parsers/ENRelativeDateFormatParser.ts +2 -2
  58. package/src/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.ts +1 -1
  59. package/src/locales/en/refiners/ENMergeRelativeDateRefiner.ts +58 -0
  60. package/src/locales/zh/hans/constants.ts +52 -0
  61. package/src/locales/zh/hans/index.ts +62 -0
  62. package/src/locales/zh/hans/parsers/ZHHansCasualDateParser.ts +128 -0
  63. package/src/locales/zh/hans/parsers/ZHHansDateParser.ts +75 -0
  64. package/src/locales/zh/hans/parsers/ZHHansDeadlineFormatParser.ts +81 -0
  65. package/src/locales/zh/hans/parsers/ZHHansRelationWeekdayParser.ts +69 -0
  66. package/src/locales/zh/hans/parsers/ZHHansTimeExpressionParser.ts +424 -0
  67. package/src/locales/zh/hans/parsers/ZHHansWeekdayParser.ts +46 -0
  68. package/src/locales/zh/hans/refiners/ZHHansMergeDateRangeRefiner.ts +7 -0
  69. package/src/locales/zh/hans/refiners/ZHHansMergeDateTimeRefiner.ts +7 -0
  70. package/src/locales/zh/hant/index.ts +63 -0
  71. package/src/locales/zh/index.ts +2 -63
  72. package/src/results.ts +2 -3
  73. package/src/timezone.ts +4 -4
  74. package/test/de/de_month_name_little_endian.test.ts +12 -0
  75. package/test/de/de_time_exp.test.ts +59 -0
  76. package/test/de/de_time_units_casual_relative.test.ts +108 -0
  77. package/test/de/de_year.test.ts +147 -0
  78. package/test/en/en_merging_relative_dates.test.ts +51 -0
  79. package/test/en/en_month.test.ts +13 -0
  80. package/test/en/en_relative.test.ts +61 -2
  81. package/test/en/en_time_units_ago.test.ts +3 -3
  82. package/test/en/en_time_units_casual_relative.test.ts +16 -0
  83. package/test/en/en_time_units_within.test.ts +2 -2
  84. package/test/en/en_timezone_exp.test.ts +3 -3
  85. package/test/fr/fr_time_units_ago.test.ts +3 -3
  86. package/test/fr/fr_time_units_within.test.ts +2 -2
  87. package/test/nl/nl_relative.test.ts +2 -2
  88. package/test/nl/nl_time_units_ago.test.ts +3 -3
  89. package/test/system_timezone.test.ts +22 -0
  90. package/test/zh/zh_hans_casual.test.ts +228 -0
  91. package/test/zh/zh_hans_date.test.ts +102 -0
  92. package/test/zh/zh_hans_deadline.test.ts +136 -0
  93. package/test/zh/zh_hans_time_exp.test.ts +182 -0
  94. package/test/zh/zh_hans_weekday.test.ts +132 -0
  95. package/test/zh/zh_hant_date.test.ts +0 -1
package/dist/results.js CHANGED
@@ -15,7 +15,6 @@ class ReferenceWithTimezone {
15
15
  input = input !== null && input !== void 0 ? input : new Date();
16
16
  if (input instanceof Date) {
17
17
  this.instant = input;
18
- this.timezoneOffset = -input.getTimezoneOffset();
19
18
  }
20
19
  else {
21
20
  this.instant = (_a = input.instant) !== null && _a !== void 0 ? _a : new Date();
@@ -113,7 +112,7 @@ class ParsingComponents {
113
112
  return true;
114
113
  }
115
114
  toString() {
116
- return `[ParsingComponents {knownValues: ${JSON.stringify(this.knownValues)}, impliedValues: ${JSON.stringify(this.impliedValues)}}]`;
115
+ return `[ParsingComponents {knownValues: ${JSON.stringify(this.knownValues)}, impliedValues: ${JSON.stringify(this.impliedValues)}}, reference: ${JSON.stringify(this.reference)}]`;
117
116
  }
118
117
  dayjs() {
119
118
  return dayjs_1.default(this.date());
@@ -191,4 +191,4 @@ export declare const TIMEZONE_ABBR_MAP: {
191
191
  YEKST: number;
192
192
  YEKT: number;
193
193
  };
194
- export declare function toTimezoneOffset(timezoneInput: string | number): number | null;
194
+ export declare function toTimezoneOffset(timezoneInput?: string | number): number | null;
package/dist/timezone.js CHANGED
@@ -117,7 +117,7 @@ exports.TIMEZONE_ABBR_MAP = {
117
117
  MHT: 720,
118
118
  MMT: 390,
119
119
  MSD: 240,
120
- MSK: 240,
120
+ MSK: 180,
121
121
  MST: -420,
122
122
  MUT: 240,
123
123
  MVT: 300,
@@ -196,12 +196,12 @@ exports.TIMEZONE_ABBR_MAP = {
196
196
  };
197
197
  function toTimezoneOffset(timezoneInput) {
198
198
  var _a;
199
- if (timezoneInput === null) {
199
+ if (timezoneInput === null || timezoneInput === undefined) {
200
200
  return null;
201
201
  }
202
202
  if (typeof timezoneInput === "number") {
203
203
  return timezoneInput;
204
204
  }
205
- return (_a = exports.TIMEZONE_ABBR_MAP[timezoneInput]) !== null && _a !== void 0 ? _a : 0;
205
+ return (_a = exports.TIMEZONE_ABBR_MAP[timezoneInput]) !== null && _a !== void 0 ? _a : null;
206
206
  }
207
207
  exports.toTimezoneOffset = toTimezoneOffset;
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "preset": "ts-jest"
16
16
  },
17
17
  "license": "MIT",
18
- "version": "2.3.5",
18
+ "version": "2.3.8",
19
19
  "directories": {
20
20
  "source": "./src",
21
21
  "test": "./test"
@@ -221,7 +221,9 @@ export default class ExtractTimezoneAbbrRefiner implements Refiner {
221
221
  }
222
222
 
223
223
  context.debug(() => {
224
- console.log(`Extracting timezone: '${timezoneAbbr}' into : ${extractedTimezoneOffset}`);
224
+ console.log(
225
+ `Extracting timezone: '${timezoneAbbr}' into: ${extractedTimezoneOffset} for: ${result.start}`
226
+ );
225
227
  });
226
228
 
227
229
  const currentTimezoneOffset = result.start.get("timezoneOffset");
@@ -240,6 +242,14 @@ export default class ExtractTimezoneAbbrRefiner implements Refiner {
240
242
  }
241
243
  }
242
244
 
245
+ if (result.start.isOnlyDate()) {
246
+ // If the time is not explicitly mentioned,
247
+ // Then, we also want to double check the abbr case (e.g. "GET" not "get")
248
+ if (timezoneAbbr != match[1]) {
249
+ return;
250
+ }
251
+ }
252
+
243
253
  result.text += match[0];
244
254
 
245
255
  if (!result.start.isCertain("timezoneOffset")) {
@@ -1,4 +1,4 @@
1
- import { OpUnitType } from "dayjs";
1
+ import { OpUnitType, QUnitType } from "dayjs";
2
2
  import { matchAnyPattern, repeatedTimeunitPattern } from "../../utils/pattern";
3
3
  import { findMostLikelyADYear } from "../../calculation/years";
4
4
  import { TimeUnits } from "../../utils/timeunits";
@@ -22,9 +22,12 @@ export const WEEKDAY_DICTIONARY: { [word: string]: number } = {
22
22
 
23
23
  export const MONTH_DICTIONARY: { [word: string]: number } = {
24
24
  "januar": 1,
25
+ "jänner": 1,
26
+ "janner": 1,
25
27
  "jan": 1,
26
28
  "jan.": 1,
27
29
  "februar": 2,
30
+ "feber": 2,
28
31
  "feb": 2,
29
32
  "feb.": 2,
30
33
  "märz": 3,
@@ -64,6 +67,10 @@ export const MONTH_DICTIONARY: { [word: string]: number } = {
64
67
 
65
68
  export const INTEGER_WORD_DICTIONARY: { [word: string]: number } = {
66
69
  "eins": 1,
70
+ "eine": 1,
71
+ "einem": 1,
72
+ "einen": 1,
73
+ "einer": 1,
67
74
  "zwei": 2,
68
75
  "drei": 3,
69
76
  "vier": 4,
@@ -79,29 +86,37 @@ export const INTEGER_WORD_DICTIONARY: { [word: string]: number } = {
79
86
  "zwoelf": 12,
80
87
  };
81
88
 
82
- export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
83
- sec: "second",
84
- second: "second",
85
- seconds: "second",
89
+ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType | QUnitType } = {
90
+ sek: "second",
91
+ sekunde: "second",
92
+ sekunden: "second",
86
93
  min: "minute",
87
- mins: "minute",
88
94
  minute: "minute",
89
- minutes: "minute",
95
+ minuten: "minute",
90
96
  h: "hour",
91
- hr: "hour",
92
- hrs: "hour",
93
- hour: "hour",
94
- hours: "hour",
95
- day: "d",
96
- days: "d",
97
- week: "week",
98
- weeks: "week",
99
- month: "month",
100
- months: "month",
101
- y: "year",
102
- yr: "year",
103
- year: "year",
104
- years: "year",
97
+ std: "hour",
98
+ stunde: "hour",
99
+ stunden: "hour",
100
+ tag: "d",
101
+ tage: "d",
102
+ tagen: "d",
103
+ woche: "week",
104
+ wochen: "week",
105
+ monat: "month",
106
+ monate: "month",
107
+ monaten: "month",
108
+ monats: "month",
109
+ quartal: "quarter",
110
+ quartals: "quarter",
111
+ quartale: "quarter",
112
+ quartalen: "quarter",
113
+ a: "year",
114
+ j: "year",
115
+ jr: "year",
116
+ jahr: "year",
117
+ jahre: "year",
118
+ jahren: "year",
119
+ jahres: "year",
105
120
  };
106
121
 
107
122
  //-----------------------------
@@ -131,7 +146,7 @@ export function parseNumberPattern(match: string): number {
131
146
 
132
147
  //-----------------------------
133
148
 
134
- export const YEAR_PATTERN = `(?:[0-9]{1,4}(?:\\s*[vn]\\.?\\s*C(?:hr)?\\.?)?)`;
149
+ export const YEAR_PATTERN = `(?:[0-9]{1,4}(?:\\s*[vn]\\.?\\s*(?:C(?:hr)?|(?:u\\.?|d\\.?(?:\\s*g\\.?)?)?\\s*Z)\\.?|\\s*(?:u\\.?|d\\.?(?:\\s*g\\.)?)\\s*Z\\.?)?)`;
135
150
  export function parseYear(match: string): number {
136
151
  if (/v/i.test(match)) {
137
152
  // v.Chr.
@@ -143,6 +158,11 @@ export function parseYear(match: string): number {
143
158
  return parseInt(match.replace(/[^0-9]+/gi, ""));
144
159
  }
145
160
 
161
+ if (/z/i.test(match)) {
162
+ // n.Chr. as "uZ" or "dgZ"
163
+ return parseInt(match.replace(/[^0-9]+/gi, ""));
164
+ }
165
+
146
166
  const rawYearNumber = parseInt(match);
147
167
  return findMostLikelyADYear(rawYearNumber);
148
168
  }
@@ -5,11 +5,13 @@ import SlashDateFormatParser from "../../common/parsers/SlashDateFormatParser";
5
5
  import ISOFormatParser from "../../common/parsers/ISOFormatParser";
6
6
  import DETimeExpressionParser from "./parsers/DETimeExpressionParser";
7
7
  import DEWeekdayParser from "./parsers/DEWeekdayParser";
8
+ import DESpecificTimeExpressionParser from "./parsers/DESpecificTimeExpressionParser";
8
9
  import DEMergeDateRangeRefiner from "./refiners/DEMergeDateRangeRefiner";
9
10
  import DEMergeDateTimeRefiner from "./refiners/DEMergeDateTimeRefiner";
10
11
  import DECasualDateParser from "./parsers/DECasualDateParser";
11
12
  import DECasualTimeParser from "./parsers/DECasualTimeParser";
12
13
  import DEMonthNameLittleEndianParser from "./parsers/DEMonthNameLittleEndianParser";
14
+ import DETimeUnitRelativeFormatParser from "./parsers/DETimeUnitRelativeFormatParser";
13
15
 
14
16
  // Shortcuts
15
17
  export const casual = new Chrono(createCasualConfiguration());
@@ -27,6 +29,7 @@ export function createCasualConfiguration(littleEndian = true): Configuration {
27
29
  const option = createConfiguration(false, littleEndian);
28
30
  option.parsers.unshift(new DECasualTimeParser());
29
31
  option.parsers.unshift(new DECasualDateParser());
32
+ option.parsers.unshift(new DETimeUnitRelativeFormatParser());
30
33
  return option;
31
34
  }
32
35
 
@@ -37,6 +40,7 @@ export function createConfiguration(strictMode = true, littleEndian = true): Con
37
40
  new ISOFormatParser(),
38
41
  new SlashDateFormatParser(littleEndian),
39
42
  new DETimeExpressionParser(),
43
+ new DESpecificTimeExpressionParser(),
40
44
  new DEMonthNameLittleEndianParser(),
41
45
  new DEWeekdayParser(),
42
46
  ],
@@ -0,0 +1,150 @@
1
+ import { Parser, ParsingContext } from "../../../chrono";
2
+ import { ParsingComponents, ParsingResult } from "../../../results";
3
+ import { Meridiem } from "../../../index";
4
+
5
+ /**
6
+ * 8h10m00s
7
+ * 8h10m00
8
+ * 8h10
9
+ * 8 Uhr
10
+ * 8h10m00s Uhr
11
+ * 8:10 Uhr
12
+ */
13
+ const FIRST_REG_PATTERN = new RegExp(
14
+ "(^|\\s|T)" +
15
+ "(?:(?:um|von)\\s*)?" +
16
+ "(\\d{1,2})(?:h|:)?" +
17
+ "(?:(\\d{1,2})(?:m|:)?)?" +
18
+ "(?:(\\d{1,2})(?:s)?)?" +
19
+ "(?:\\s*Uhr)?" +
20
+ "(?:\\s*(morgens|vormittags|nachmittags|abends|nachts|am\\s+(?:Morgen|Vormittag|Nachmittag|Abend)|in\\s+der\\s+Nacht))?" +
21
+ "(?=\\W|$)",
22
+ "i"
23
+ );
24
+
25
+ const SECOND_REG_PATTERN = new RegExp(
26
+ "^\\s*(\\-|\\–|\\~|\\〜|bis(?:\\s+um)?|\\?)\\s*" +
27
+ "(\\d{1,2})(?:h|:)?" +
28
+ "(?:(\\d{1,2})(?:m|:)?)?" +
29
+ "(?:(\\d{1,2})(?:s)?)?" +
30
+ "(?:\\s*Uhr)?" +
31
+ "(?:\\s*(morgens|vormittags|nachmittags|abends|nachts|am\\s+(?:Morgen|Vormittag|Nachmittag|Abend)|in\\s+der\\s+Nacht))?" +
32
+ "(?=\\W|$)",
33
+ "i"
34
+ );
35
+
36
+ const HOUR_GROUP = 2;
37
+ const MINUTE_GROUP = 3;
38
+ const SECOND_GROUP = 4;
39
+ const AM_PM_HOUR_GROUP = 5;
40
+
41
+ export default class DESpecificTimeExpressionParser implements Parser {
42
+ pattern(context): RegExp {
43
+ return FIRST_REG_PATTERN;
44
+ }
45
+
46
+ extract(context: ParsingContext, match: RegExpMatchArray): ParsingResult | null {
47
+ const result = context.createParsingResult(match.index + match[1].length, match[0].substring(match[1].length));
48
+
49
+ // This looks more like a year e.g. 2020
50
+ if (result.text.match(/^\d{4}$/)) {
51
+ match.index += match[0].length;
52
+ return null;
53
+ }
54
+
55
+ result.start = DESpecificTimeExpressionParser.extractTimeComponent(result.start.clone(), match);
56
+ if (!result.start) {
57
+ match.index += match[0].length;
58
+ return null;
59
+ }
60
+
61
+ const remainingText = context.text.substring(match.index + match[0].length);
62
+ const secondMatch = SECOND_REG_PATTERN.exec(remainingText);
63
+ if (secondMatch) {
64
+ result.end = DESpecificTimeExpressionParser.extractTimeComponent(result.start.clone(), secondMatch);
65
+ if (result.end) {
66
+ result.text += secondMatch[0];
67
+ }
68
+ }
69
+
70
+ return result;
71
+ }
72
+
73
+ private static extractTimeComponent(
74
+ extractingComponents: ParsingComponents,
75
+ match: RegExpMatchArray
76
+ ): ParsingComponents | null {
77
+ let hour = 0;
78
+ let minute = 0;
79
+ let meridiem = null;
80
+
81
+ // ----- Hours
82
+ hour = parseInt(match[HOUR_GROUP]);
83
+
84
+ // ----- Minutes
85
+ if (match[MINUTE_GROUP] != null) {
86
+ minute = parseInt(match[MINUTE_GROUP]);
87
+ }
88
+
89
+ if (minute >= 60 || hour > 24) {
90
+ return null;
91
+ }
92
+
93
+ if (hour >= 12) {
94
+ meridiem = Meridiem.PM;
95
+ }
96
+
97
+ // ----- AM & PM
98
+ if (match[AM_PM_HOUR_GROUP] != null) {
99
+ if (hour > 12) return null;
100
+ const ampm = match[AM_PM_HOUR_GROUP].toLowerCase();
101
+ if (ampm.match(/morgen|vormittag/)) {
102
+ meridiem = Meridiem.AM;
103
+ if (hour == 12) {
104
+ hour = 0;
105
+ }
106
+ }
107
+
108
+ if (ampm.match(/nachmittag|abend/)) {
109
+ meridiem = Meridiem.PM;
110
+ if (hour != 12) {
111
+ hour += 12;
112
+ }
113
+ }
114
+
115
+ if (ampm.match(/nacht/)) {
116
+ if (hour == 12) {
117
+ meridiem = Meridiem.AM;
118
+ hour = 0;
119
+ } else if (hour < 6) {
120
+ meridiem = Meridiem.AM;
121
+ } else {
122
+ meridiem = Meridiem.PM;
123
+ hour += 12;
124
+ }
125
+ }
126
+ }
127
+
128
+ extractingComponents.assign("hour", hour);
129
+ extractingComponents.assign("minute", minute);
130
+ if (meridiem !== null) {
131
+ extractingComponents.assign("meridiem", meridiem);
132
+ } else {
133
+ if (hour < 12) {
134
+ extractingComponents.imply("meridiem", Meridiem.AM);
135
+ } else {
136
+ extractingComponents.imply("meridiem", Meridiem.PM);
137
+ }
138
+ }
139
+
140
+ // ----- Second
141
+ if (match[SECOND_GROUP] != null) {
142
+ const second = parseInt(match[SECOND_GROUP]);
143
+ if (second >= 60) return null;
144
+
145
+ extractingComponents.assign("second", second);
146
+ }
147
+
148
+ return extractingComponents;
149
+ }
150
+ }
@@ -1,7 +1,6 @@
1
1
  import { AbstractTimeExpressionParser } from "../../../common/parsers/AbstractTimeExpressionParser";
2
2
  import { ParsingComponents } from "../../../results";
3
3
  import { ParsingContext } from "../../../chrono";
4
- import { Meridiem } from "../../../index";
5
4
 
6
5
  export default class DETimeExpressionParser extends AbstractTimeExpressionParser {
7
6
  primaryPrefix(): string {
@@ -12,30 +11,12 @@ export default class DETimeExpressionParser extends AbstractTimeExpressionParser
12
11
  return "\\s*(?:\\-|\\–|\\~|\\〜|bis)\\s*";
13
12
  }
14
13
 
15
- primarySuffix(): string {
16
- return "(?:\\s*uhr)?(?:\\s*(?:morgens|vormittags|nachmittags|abends|nachts))?(?=\\W|$)";
17
- }
18
-
19
14
  extractPrimaryTimeComponents(context: ParsingContext, match: RegExpMatchArray): ParsingComponents | null {
20
- const components = super.extractPrimaryTimeComponents(context, match);
21
- if (components) {
22
- if (match[0].endsWith("morgens") || match[0].endsWith("vormittags")) {
23
- components.assign("meridiem", Meridiem.AM);
24
- const hour = components.get("hour");
25
- if (hour < 12) {
26
- components.assign("hour", components.get("hour"));
27
- }
28
- }
29
-
30
- if (match[0].endsWith("nachmittags") || match[0].endsWith("abends") || match[0].endsWith("nachts")) {
31
- components.assign("meridiem", Meridiem.PM);
32
- const hour = components.get("hour");
33
- if (hour < 12) {
34
- components.assign("hour", components.get("hour") + 12);
35
- }
36
- }
15
+ // This looks more like a year e.g. 2020
16
+ if (match[0].match(/^\s*\d{4}\s*$/)) {
17
+ return null;
37
18
  }
38
19
 
39
- return components;
20
+ return super.extractPrimaryTimeComponents(context, match);
40
21
  }
41
22
  }
@@ -0,0 +1,42 @@
1
+ import { ParsingContext } from "../../../chrono";
2
+ import { NUMBER_PATTERN, parseNumberPattern, TIME_UNIT_DICTIONARY } from "../constants";
3
+ import { ParsingComponents } from "../../../results";
4
+ import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
5
+ import { reverseTimeUnits } from "../../../utils/timeunits";
6
+ import { matchAnyPattern } from "../../../utils/pattern";
7
+
8
+ export default class DETimeUnitAgoFormatParser extends AbstractParserWithWordBoundaryChecking {
9
+ constructor() {
10
+ super();
11
+ }
12
+
13
+ innerPattern(): RegExp {
14
+ return new RegExp(
15
+ `(?:\\s*((?:nächste|kommende|folgende|letzte|vergangene|vorige|vor(?:her|an)gegangene)(?:s|n|m|r)?|vor|in)\\s*)?` +
16
+ `(${NUMBER_PATTERN})?` +
17
+ `(?:\\s*(nächste|kommende|folgende|letzte|vergangene|vorige|vor(?:her|an)gegangene)(?:s|n|m|r)?)?` +
18
+ `\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})`,
19
+ "i"
20
+ );
21
+ }
22
+
23
+ innerExtract(context: ParsingContext, match: RegExpMatchArray) {
24
+ const num = match[2] ? parseNumberPattern(match[2]) : 1;
25
+ const unit = TIME_UNIT_DICTIONARY[match[4].toLowerCase()];
26
+ let timeUnits = {};
27
+ timeUnits[unit] = num;
28
+
29
+ // Modifier
30
+ let modifier = match[1] || match[3] || "";
31
+ modifier = modifier.toLowerCase();
32
+ if (!modifier) {
33
+ return;
34
+ }
35
+
36
+ if (/vor/.test(modifier) || /letzte/.test(modifier) || /vergangen/.test(modifier)) {
37
+ timeUnits = reverseTimeUnits(timeUnits);
38
+ }
39
+
40
+ return ParsingComponents.createRelativeFromReference(context.reference, timeUnits);
41
+ }
42
+ }
@@ -1,4 +1,4 @@
1
- import { OpUnitType } from "dayjs";
1
+ import { OpUnitType, QUnitType } from "dayjs";
2
2
  import { matchAnyPattern, repeatedTimeunitPattern } from "../../utils/pattern";
3
3
  import { findMostLikelyADYear } from "../../calculation/years";
4
4
  import { TimeUnits } from "../../utils/timeunits";
@@ -133,7 +133,7 @@ export const ORDINAL_WORD_DICTIONARY: { [word: string]: number } = {
133
133
  "thirty-first": 31,
134
134
  };
135
135
 
136
- export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
136
+ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType | QUnitType } = {
137
137
  sec: "second",
138
138
  second: "second",
139
139
  seconds: "second",
@@ -152,6 +152,9 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType } = {
152
152
  weeks: "week",
153
153
  month: "month",
154
154
  months: "month",
155
+ qtr: "quarter",
156
+ quarter: "quarter",
157
+ quarters: "quarter",
155
158
  y: "year",
156
159
  yr: "year",
157
160
  year: "year",
@@ -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
  );
@@ -36,8 +36,6 @@ export default class ENMonthNameLittleEndianParser extends AbstractParserWithWor
36
36
  }
37
37
 
38
38
  innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingResult {
39
- //console.log(match)
40
-
41
39
  const result = context.createParsingResult(match.index, match[0]);
42
40
 
43
41
  const month = MONTH_DICTIONARY[match[MONTH_NAME_GROUP].toLowerCase()];
@@ -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|next|last|past)\\s*(${matchAnyPattern(TIME_UNIT_DICTIONARY)})(?=\\s*)` + "(?=\\W|$)",
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
+ }
@@ -0,0 +1,52 @@
1
+ export const NUMBER = {
2
+ "零": 0,
3
+ "〇": 0,
4
+ "一": 1,
5
+ "二": 2,
6
+ "两": 2,
7
+ "三": 3,
8
+ "四": 4,
9
+ "五": 5,
10
+ "六": 6,
11
+ "七": 7,
12
+ "八": 8,
13
+ "九": 9,
14
+ "十": 10,
15
+ };
16
+
17
+ export const WEEKDAY_OFFSET = {
18
+ "天": 0,
19
+ "日": 0,
20
+ "一": 1,
21
+ "二": 2,
22
+ "三": 3,
23
+ "四": 4,
24
+ "五": 5,
25
+ "六": 6,
26
+ };
27
+
28
+ export function zhStringToNumber(text: string) {
29
+ let number = 0;
30
+
31
+ for (let i = 0; i < text.length; i++) {
32
+ const char = text[i];
33
+ if (char === "十") {
34
+ number = number === 0 ? NUMBER[char] : number * NUMBER[char];
35
+ } else {
36
+ number += NUMBER[char];
37
+ }
38
+ }
39
+
40
+ return number;
41
+ }
42
+
43
+ export function zhStringToYear(text: string) {
44
+ let string = "";
45
+
46
+ for (let i = 0; i < text.length; i++) {
47
+ const char = text[i];
48
+ string = string + NUMBER[char];
49
+ }
50
+
51
+ return parseInt(string);
52
+ }