chrono-node 2.6.2 → 2.6.3

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 (97) hide show
  1. package/README.md +32 -2
  2. package/dist/cjs/common/refiners/AbstractMergeDateRangeRefiner.js +15 -7
  3. package/dist/cjs/common/refiners/AbstractMergeDateRangeRefiner.js.map +1 -1
  4. package/dist/cjs/common/refiners/ExtractTimezoneAbbrRefiner.d.ts +3 -4
  5. package/dist/cjs/common/refiners/ExtractTimezoneAbbrRefiner.js +6 -196
  6. package/dist/cjs/common/refiners/ExtractTimezoneAbbrRefiner.js.map +1 -1
  7. package/dist/cjs/common/refiners/ForwardDateRefiner.js +14 -14
  8. package/dist/cjs/common/refiners/ForwardDateRefiner.js.map +1 -1
  9. package/dist/cjs/configurations.js +1 -1
  10. package/dist/cjs/configurations.js.map +1 -1
  11. package/dist/cjs/index.d.ts +24 -3
  12. package/dist/cjs/index.js +16 -1
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/locales/en/constants.d.ts +4 -0
  15. package/dist/cjs/locales/en/constants.js +5 -18
  16. package/dist/cjs/locales/en/constants.js.map +1 -1
  17. package/dist/cjs/locales/en/index.js +1 -1
  18. package/dist/cjs/locales/en/index.js.map +1 -1
  19. package/dist/cjs/locales/en/parsers/ENTimeUnitAgoFormatParser.js +2 -2
  20. package/dist/cjs/locales/en/parsers/ENTimeUnitAgoFormatParser.js.map +1 -1
  21. package/dist/cjs/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.d.ts +2 -0
  22. package/dist/cjs/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js +6 -1
  23. package/dist/cjs/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js.map +1 -1
  24. package/dist/cjs/locales/en/parsers/ENTimeUnitLaterFormatParser.js +1 -1
  25. package/dist/cjs/locales/en/parsers/ENTimeUnitLaterFormatParser.js.map +1 -1
  26. package/dist/cjs/locales/en/parsers/ENTimeUnitWithinFormatParser.d.ts +2 -0
  27. package/dist/cjs/locales/en/parsers/ENTimeUnitWithinFormatParser.js +10 -1
  28. package/dist/cjs/locales/en/parsers/ENTimeUnitWithinFormatParser.js.map +1 -1
  29. package/dist/cjs/locales/ru/constants.js +4 -1
  30. package/dist/cjs/locales/ru/constants.js.map +1 -1
  31. package/dist/cjs/results.d.ts +1 -1
  32. package/dist/cjs/results.js +3 -3
  33. package/dist/cjs/results.js.map +1 -1
  34. package/dist/cjs/timezone.d.ts +5 -194
  35. package/dist/cjs/timezone.js +80 -7
  36. package/dist/cjs/timezone.js.map +1 -1
  37. package/dist/esm/common/refiners/AbstractMergeDateRangeRefiner.js +15 -7
  38. package/dist/esm/common/refiners/AbstractMergeDateRangeRefiner.js.map +1 -1
  39. package/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.d.ts +3 -4
  40. package/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.js +6 -196
  41. package/dist/esm/common/refiners/ExtractTimezoneAbbrRefiner.js.map +1 -1
  42. package/dist/esm/common/refiners/ForwardDateRefiner.js +14 -14
  43. package/dist/esm/common/refiners/ForwardDateRefiner.js.map +1 -1
  44. package/dist/esm/configurations.js +1 -1
  45. package/dist/esm/configurations.js.map +1 -1
  46. package/dist/esm/index.d.ts +24 -3
  47. package/dist/esm/index.js +15 -0
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/locales/en/constants.d.ts +4 -0
  50. package/dist/esm/locales/en/constants.js +21 -0
  51. package/dist/esm/locales/en/constants.js.map +1 -1
  52. package/dist/esm/locales/en/index.js +1 -1
  53. package/dist/esm/locales/en/index.js.map +1 -1
  54. package/dist/esm/locales/en/parsers/ENTimeUnitAgoFormatParser.js +3 -3
  55. package/dist/esm/locales/en/parsers/ENTimeUnitAgoFormatParser.js.map +1 -1
  56. package/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.d.ts +2 -0
  57. package/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js +7 -2
  58. package/dist/esm/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.js.map +1 -1
  59. package/dist/esm/locales/en/parsers/ENTimeUnitLaterFormatParser.js +2 -2
  60. package/dist/esm/locales/en/parsers/ENTimeUnitLaterFormatParser.js.map +1 -1
  61. package/dist/esm/locales/en/parsers/ENTimeUnitWithinFormatParser.d.ts +2 -0
  62. package/dist/esm/locales/en/parsers/ENTimeUnitWithinFormatParser.js +11 -2
  63. package/dist/esm/locales/en/parsers/ENTimeUnitWithinFormatParser.js.map +1 -1
  64. package/dist/esm/locales/ru/constants.js +4 -1
  65. package/dist/esm/locales/ru/constants.js.map +1 -1
  66. package/dist/esm/results.d.ts +1 -1
  67. package/dist/esm/results.js +3 -3
  68. package/dist/esm/results.js.map +1 -1
  69. package/dist/esm/timezone.d.ts +5 -194
  70. package/dist/esm/timezone.js +74 -6
  71. package/dist/esm/timezone.js.map +1 -1
  72. package/package.json +4 -5
  73. package/src/common/refiners/AbstractMergeDateRangeRefiner.ts +13 -8
  74. package/src/common/refiners/ExtractTimezoneAbbrRefiner.ts +7 -201
  75. package/src/common/refiners/ForwardDateRefiner.ts +18 -16
  76. package/src/configurations.ts +3 -1
  77. package/src/index.ts +46 -2
  78. package/src/locales/en/constants.ts +30 -0
  79. package/src/locales/en/index.ts +1 -1
  80. package/src/locales/en/parsers/ENTimeUnitAgoFormatParser.ts +3 -3
  81. package/src/locales/en/parsers/ENTimeUnitCasualRelativeFormatParser.ts +10 -2
  82. package/src/locales/en/parsers/ENTimeUnitLaterFormatParser.ts +5 -2
  83. package/src/locales/en/parsers/ENTimeUnitWithinFormatParser.ts +16 -3
  84. package/src/locales/ru/constants.ts +4 -1
  85. package/src/results.ts +3 -3
  86. package/src/timezone.ts +124 -7
  87. package/test/de/de_time_exp.test.ts +7 -7
  88. package/test/en/en_month.test.ts +70 -0
  89. package/test/en/en_time_units_ago.test.ts +12 -0
  90. package/test/en/en_time_units_casual_relative.test.ts +18 -0
  91. package/test/en/en_time_units_later.test.ts +13 -10
  92. package/test/en/en_time_units_within.test.ts +12 -1
  93. package/test/en/en_timezone_exp.test.ts +142 -0
  94. package/test/en/en_weekday.test.ts +26 -0
  95. package/test/en/negative_cases.test.ts +2 -0
  96. package/test/fr/fr_time_exp.test.ts +2 -2
  97. package/test/system.test.ts +23 -1
@@ -16,13 +16,13 @@ export default abstract class AbstractMergeDateRangeRefiner extends MergingRefin
16
16
  if (!fromResult.start.isOnlyWeekdayComponent() && !toResult.start.isOnlyWeekdayComponent()) {
17
17
  toResult.start.getCertainComponents().forEach((key) => {
18
18
  if (!fromResult.start.isCertain(key)) {
19
- fromResult.start.assign(key, toResult.start.get(key));
19
+ fromResult.start.imply(key, toResult.start.get(key));
20
20
  }
21
21
  });
22
22
 
23
23
  fromResult.start.getCertainComponents().forEach((key) => {
24
24
  if (!toResult.start.isCertain(key)) {
25
- toResult.start.assign(key, fromResult.start.get(key));
25
+ toResult.start.imply(key, fromResult.start.get(key));
26
26
  }
27
27
  });
28
28
  }
@@ -30,17 +30,22 @@ export default abstract class AbstractMergeDateRangeRefiner extends MergingRefin
30
30
  if (fromResult.start.date().getTime() > toResult.start.date().getTime()) {
31
31
  let fromMoment = fromResult.start.dayjs();
32
32
  let toMoment = toResult.start.dayjs();
33
-
34
- if (fromResult.start.isOnlyWeekdayComponent() && fromMoment.add(-7, "days").isBefore(toMoment)) {
33
+ if (toResult.start.isOnlyWeekdayComponent() && toMoment.add(7, "days").isAfter(fromMoment)) {
34
+ toMoment = toMoment.add(7, "days");
35
+ toResult.start.imply("day", toMoment.date());
36
+ toResult.start.imply("month", toMoment.month() + 1);
37
+ toResult.start.imply("year", toMoment.year());
38
+ } else if (fromResult.start.isOnlyWeekdayComponent() && fromMoment.add(-7, "days").isBefore(toMoment)) {
35
39
  fromMoment = fromMoment.add(-7, "days");
36
40
  fromResult.start.imply("day", fromMoment.date());
37
41
  fromResult.start.imply("month", fromMoment.month() + 1);
38
42
  fromResult.start.imply("year", fromMoment.year());
39
- } else if (toResult.start.isOnlyWeekdayComponent() && toMoment.add(7, "days").isAfter(fromMoment)) {
40
- toMoment = toMoment.add(7, "days");
41
- toResult.start.imply("day", toMoment.date());
42
- toResult.start.imply("month", toMoment.month() + 1);
43
+ } else if (toResult.start.isDateWithUnknownYear() && toMoment.add(1, "years").isAfter(fromMoment)) {
44
+ toMoment = toMoment.add(1, "years");
43
45
  toResult.start.imply("year", toMoment.year());
46
+ } else if (fromResult.start.isDateWithUnknownYear() && fromMoment.add(-1, "years").isBefore(toMoment)) {
47
+ fromMoment = fromMoment.add(-1, "years");
48
+ fromResult.start.imply("year", fromMoment.year());
44
49
  } else {
45
50
  [toResult, fromResult] = [fromResult, toResult];
46
51
  }
@@ -1,208 +1,13 @@
1
1
  // Map ABBR -> Offset in minute
2
2
  import { ParsingContext, Refiner } from "../../chrono";
3
+ import { TimezoneAbbrMap } from "../../index";
3
4
  import { ParsingResult } from "../../results";
5
+ import { toTimezoneOffset } from "../../timezone";
4
6
 
5
7
  const TIMEZONE_NAME_PATTERN = new RegExp("^\\s*,?\\s*\\(?([A-Z]{2,4})\\)?(?=\\W|$)", "i");
6
- const DEFAULT_TIMEZONE_ABBR_MAP = {
7
- ACDT: 630,
8
- ACST: 570,
9
- ADT: -180,
10
- AEDT: 660,
11
- AEST: 600,
12
- AFT: 270,
13
- AKDT: -480,
14
- AKST: -540,
15
- ALMT: 360,
16
- AMST: -180,
17
- AMT: -240,
18
- ANAST: 720,
19
- ANAT: 720,
20
- AQTT: 300,
21
- ART: -180,
22
- AST: -240,
23
- AWDT: 540,
24
- AWST: 480,
25
- AZOST: 0,
26
- AZOT: -60,
27
- AZST: 300,
28
- AZT: 240,
29
- BNT: 480,
30
- BOT: -240,
31
- BRST: -120,
32
- BRT: -180,
33
- BST: 60,
34
- BTT: 360,
35
- CAST: 480,
36
- CAT: 120,
37
- CCT: 390,
38
- CDT: -300,
39
- CEST: 120,
40
- CET: 60,
41
- CHADT: 825,
42
- CHAST: 765,
43
- CKT: -600,
44
- CLST: -180,
45
- CLT: -240,
46
- COT: -300,
47
- CST: -360,
48
- CVT: -60,
49
- CXT: 420,
50
- ChST: 600,
51
- DAVT: 420,
52
- EASST: -300,
53
- EAST: -360,
54
- EAT: 180,
55
- ECT: -300,
56
- EDT: -240,
57
- EEST: 180,
58
- EET: 120,
59
- EGST: 0,
60
- EGT: -60,
61
- EST: -300,
62
- ET: -300,
63
- FJST: 780,
64
- FJT: 720,
65
- FKST: -180,
66
- FKT: -240,
67
- FNT: -120,
68
- GALT: -360,
69
- GAMT: -540,
70
- GET: 240,
71
- GFT: -180,
72
- GILT: 720,
73
- GMT: 0,
74
- GST: 240,
75
- GYT: -240,
76
- HAA: -180,
77
- HAC: -300,
78
- HADT: -540,
79
- HAE: -240,
80
- HAP: -420,
81
- HAR: -360,
82
- HAST: -600,
83
- HAT: -90,
84
- HAY: -480,
85
- HKT: 480,
86
- HLV: -210,
87
- HNA: -240,
88
- HNC: -360,
89
- HNE: -300,
90
- HNP: -480,
91
- HNR: -420,
92
- HNT: -150,
93
- HNY: -540,
94
- HOVT: 420,
95
- ICT: 420,
96
- IDT: 180,
97
- IOT: 360,
98
- IRDT: 270,
99
- IRKST: 540,
100
- IRKT: 540,
101
- IRST: 210,
102
- IST: 330,
103
- JST: 540,
104
- KGT: 360,
105
- KRAST: 480,
106
- KRAT: 480,
107
- KST: 540,
108
- KUYT: 240,
109
- LHDT: 660,
110
- LHST: 630,
111
- LINT: 840,
112
- MAGST: 720,
113
- MAGT: 720,
114
- MART: -510,
115
- MAWT: 300,
116
- MDT: -360,
117
- MESZ: 120,
118
- MEZ: 60,
119
- MHT: 720,
120
- MMT: 390,
121
- MSD: 240,
122
- MSK: 240,
123
- MST: -420,
124
- MUT: 240,
125
- MVT: 300,
126
- MYT: 480,
127
- NCT: 660,
128
- NDT: -90,
129
- NFT: 690,
130
- NOVST: 420,
131
- NOVT: 360,
132
- NPT: 345,
133
- NST: -150,
134
- NUT: -660,
135
- NZDT: 780,
136
- NZST: 720,
137
- OMSST: 420,
138
- OMST: 420,
139
- PDT: -420,
140
- PET: -300,
141
- PETST: 720,
142
- PETT: 720,
143
- PGT: 600,
144
- PHOT: 780,
145
- PHT: 480,
146
- PKT: 300,
147
- PMDT: -120,
148
- PMST: -180,
149
- PONT: 660,
150
- PST: -480,
151
- PT: -480,
152
- PWT: 540,
153
- PYST: -180,
154
- PYT: -240,
155
- RET: 240,
156
- SAMT: 240,
157
- SAST: 120,
158
- SBT: 660,
159
- SCT: 240,
160
- SGT: 480,
161
- SRT: -180,
162
- SST: -660,
163
- TAHT: -600,
164
- TFT: 300,
165
- TJT: 300,
166
- TKT: 780,
167
- TLT: 540,
168
- TMT: 300,
169
- TVT: 720,
170
- ULAT: 480,
171
- UTC: 0,
172
- UYST: -120,
173
- UYT: -180,
174
- UZT: 300,
175
- VET: -210,
176
- VLAST: 660,
177
- VLAT: 660,
178
- VUT: 660,
179
- WAST: 120,
180
- WAT: 60,
181
- WEST: 60,
182
- WESZ: 60,
183
- WET: 0,
184
- WEZ: 0,
185
- WFT: 720,
186
- WGST: -120,
187
- WGT: -180,
188
- WIB: 420,
189
- WIT: 540,
190
- WITA: 480,
191
- WST: 780,
192
- WT: 0,
193
- YAKST: 600,
194
- YAKT: 600,
195
- YAPT: 600,
196
- YEKST: 360,
197
- YEKT: 360,
198
- };
199
8
 
200
9
  export default class ExtractTimezoneAbbrRefiner implements Refiner {
201
- private readonly timezone: { string: number };
202
-
203
- constructor(timezoneOverrides?: { string: number }) {
204
- this.timezone = { ...DEFAULT_TIMEZONE_ABBR_MAP, ...timezoneOverrides };
205
- }
10
+ constructor(private readonly timezoneOverrides?: TimezoneAbbrMap) {}
206
11
 
207
12
  refine(context: ParsingContext, results: ParsingResult[]): ParsingResult[] {
208
13
  const timezoneOverrides = context.option.timezones ?? {};
@@ -215,11 +20,12 @@ export default class ExtractTimezoneAbbrRefiner implements Refiner {
215
20
  }
216
21
 
217
22
  const timezoneAbbr = match[1].toUpperCase();
218
- const extractedTimezoneOffset = timezoneOverrides[timezoneAbbr] ?? this.timezone[timezoneAbbr] ?? null;
219
- if (extractedTimezoneOffset === null) {
23
+ const refDate = result.start.date() ?? result.refDate ?? new Date();
24
+ const tzOverrides = { ...this.timezoneOverrides, ...timezoneOverrides };
25
+ const extractedTimezoneOffset = toTimezoneOffset(timezoneAbbr, refDate, tzOverrides);
26
+ if (extractedTimezoneOffset == null) {
220
27
  return;
221
28
  }
222
-
223
29
  context.debug(() => {
224
30
  console.log(
225
31
  `Extracting timezone: '${timezoneAbbr}' into: ${extractedTimezoneOffset} for: ${result.start}`
@@ -30,22 +30,6 @@ export default class ForwardDateRefiner implements Refiner {
30
30
  }
31
31
  }
32
32
 
33
- if (result.start.isOnlyDayMonthComponent() && refMoment.isAfter(result.start.dayjs())) {
34
- for (let i = 0; i < 3 && refMoment.isAfter(result.start.dayjs()); i++) {
35
- result.start.imply("year", result.start.get("year") + 1);
36
- context.debug(() => {
37
- console.log(`Forward yearly adjusted for ${result} (${result.start})`);
38
- });
39
-
40
- if (result.end && !result.end.isCertain("year")) {
41
- result.end.imply("year", result.end.get("year") + 1);
42
- context.debug(() => {
43
- console.log(`Forward yearly adjusted for ${result} (${result.end})`);
44
- });
45
- }
46
- }
47
- }
48
-
49
33
  if (result.start.isOnlyWeekdayComponent() && refMoment.isAfter(result.start.dayjs())) {
50
34
  if (refMoment.day() >= result.start.get("weekday")) {
51
35
  refMoment = refMoment.day(result.start.get("weekday") + 7);
@@ -76,6 +60,24 @@ export default class ForwardDateRefiner implements Refiner {
76
60
  });
77
61
  }
78
62
  }
63
+
64
+ // In case where we know the month, but not which year (e.g. "in December", "25th December"),
65
+ // try move to another year
66
+ if (result.start.isDateWithUnknownYear() && refMoment.isAfter(result.start.dayjs())) {
67
+ for (let i = 0; i < 3 && refMoment.isAfter(result.start.dayjs()); i++) {
68
+ result.start.imply("year", result.start.get("year") + 1);
69
+ context.debug(() => {
70
+ console.log(`Forward yearly adjusted for ${result} (${result.start})`);
71
+ });
72
+
73
+ if (result.end && !result.end.isCertain("year")) {
74
+ result.end.imply("year", result.end.get("year") + 1);
75
+ context.debug(() => {
76
+ console.log(`Forward yearly adjusted for ${result} (${result.end})`);
77
+ });
78
+ }
79
+ }
80
+ }
79
81
  });
80
82
 
81
83
  return results;
@@ -12,10 +12,12 @@ export function includeCommonConfiguration(configuration: Configuration, strictM
12
12
  configuration.parsers.unshift(new ISOFormatParser());
13
13
 
14
14
  configuration.refiners.unshift(new MergeWeekdayComponentRefiner());
15
- configuration.refiners.unshift(new ExtractTimezoneAbbrRefiner());
16
15
  configuration.refiners.unshift(new ExtractTimezoneOffsetRefiner());
17
16
  configuration.refiners.unshift(new OverlapRemovalRefiner());
18
17
 
18
+ // Unlike ExtractTimezoneOffsetRefiner, this refiner relies on knowing both date and time in cases where the tz
19
+ // is ambiguous (in terms of DST/non-DST). It therefore needs to be applied as late as possible in the parsing.
20
+ configuration.refiners.push(new ExtractTimezoneAbbrRefiner());
19
21
  configuration.refiners.push(new OverlapRemovalRefiner());
20
22
  configuration.refiners.push(new ForwardDateRefiner());
21
23
  configuration.refiners.push(new UnlikelyFormatFilter(strictMode));
package/src/index.ts CHANGED
@@ -13,9 +13,10 @@ export interface ParsingOption {
13
13
  forwardDate?: boolean;
14
14
 
15
15
  /**
16
- * Additional timezone keywords for the parsers to recognize
16
+ * Additional timezone keywords for the parsers to recognize.
17
+ * Any value provided will override the default handling of that value.
17
18
  */
18
- timezones?: { [tzKeyword: string]: number };
19
+ timezones?: TimezoneAbbrMap;
19
20
 
20
21
  /**
21
22
  * Internal debug event handler.
@@ -24,6 +25,34 @@ export interface ParsingOption {
24
25
  debug?: DebugHandler | DebugConsume;
25
26
  }
26
27
 
28
+ /**
29
+ * Some timezone abbreviations are ambiguous in that they refer to different offsets
30
+ * depending on the time of year — daylight savings time (DST), or non-DST. This interface
31
+ * allows defining such timezones
32
+ */
33
+ export interface AmbiguousTimezoneMap {
34
+ timezoneOffsetDuringDst: number;
35
+ timezoneOffsetNonDst: number;
36
+ /**
37
+ * Return the start date of DST for the given year.
38
+ * timezone.ts contains helper methods for common such rules.
39
+ */
40
+ dstStart: (year: number) => Date;
41
+ /**
42
+ * Return the end date of DST for the given year.
43
+ * timezone.ts contains helper methods for common such rules.
44
+ */
45
+ dstEnd: (year: number) => Date;
46
+ }
47
+
48
+ /**
49
+ * A map describing how timezone abbreviations should map to time offsets.
50
+ * Supports both unambigous mappings abbreviation => offset,
51
+ * and ambiguous mappings, where the offset will depend on whether the
52
+ * time in question is during daylight savings time or not.
53
+ */
54
+ export type TimezoneAbbrMap = { [key: string]: number | AmbiguousTimezoneMap };
55
+
27
56
  export interface ParsingReference {
28
57
  /**
29
58
  * Reference date. The instant (JavaScript Date object) when the input is written or mention.
@@ -110,6 +139,21 @@ export enum Weekday {
110
139
  SATURDAY = 6,
111
140
  }
112
141
 
142
+ export enum Month {
143
+ JANUARY = 1,
144
+ FEBRUARY = 2,
145
+ MARCH = 3,
146
+ APRIL = 4,
147
+ MAY = 5,
148
+ JUNE = 6,
149
+ JULY = 7,
150
+ AUGUST = 8,
151
+ SEPTEMBER = 9,
152
+ OCTOBER = 10,
153
+ NOVEMBER = 11,
154
+ DECEMBER = 12,
155
+ }
156
+
113
157
  // Export all locales
114
158
  import * as de from "./locales/de";
115
159
  import * as fr from "./locales/fr";
@@ -134,6 +134,25 @@ export const ORDINAL_WORD_DICTIONARY: { [word: string]: number } = {
134
134
  "thirty-first": 31,
135
135
  };
136
136
 
137
+ export const TIME_UNIT_DICTIONARY_NO_ABBR: { [word: string]: OpUnitType | QUnitType } = {
138
+ second: "second",
139
+ seconds: "second",
140
+ minute: "minute",
141
+ minutes: "minute",
142
+ hour: "hour",
143
+ hours: "hour",
144
+ day: "d",
145
+ days: "d",
146
+ week: "week",
147
+ weeks: "week",
148
+ month: "month",
149
+ months: "month",
150
+ quarter: "quarter",
151
+ quarters: "quarter",
152
+ year: "year",
153
+ years: "year",
154
+ };
155
+
137
156
  export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType | QUnitType } = {
138
157
  s: "second",
139
158
  sec: "second",
@@ -167,6 +186,9 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType | QUnitType } =
167
186
  yr: "year",
168
187
  year: "year",
169
188
  years: "year",
189
+ // Also, merge the entries from the full-name dictionary.
190
+ // We leave the duplicated entries for readability.
191
+ ...TIME_UNIT_DICTIONARY_NO_ABBR,
170
192
  };
171
193
 
172
194
  //-----------------------------
@@ -238,7 +260,15 @@ export function parseYear(match: string): number {
238
260
  const SINGLE_TIME_UNIT_PATTERN = `(${NUMBER_PATTERN})\\s{0,3}(${matchAnyPattern(TIME_UNIT_DICTIONARY)})`;
239
261
  const SINGLE_TIME_UNIT_REGEX = new RegExp(SINGLE_TIME_UNIT_PATTERN, "i");
240
262
 
263
+ const SINGLE_TIME_UNIT_NO_ABBR_PATTERN = `(${NUMBER_PATTERN})\\s{0,3}(${matchAnyPattern(
264
+ TIME_UNIT_DICTIONARY_NO_ABBR
265
+ )})`;
266
+
241
267
  export const TIME_UNITS_PATTERN = repeatedTimeunitPattern(`(?:(?:about|around)\\s{0,3})?`, SINGLE_TIME_UNIT_PATTERN);
268
+ export const TIME_UNITS_NO_ABBR_PATTERN = repeatedTimeunitPattern(
269
+ `(?:(?:about|around)\\s{0,3})?`,
270
+ SINGLE_TIME_UNIT_NO_ABBR_PATTERN
271
+ );
242
272
 
243
273
  export function parseTimeUnits(timeunitText): TimeUnits {
244
274
  const fragments = {};
@@ -82,7 +82,7 @@ export function createConfiguration(strictMode = true, littleEndian = false): Co
82
82
  {
83
83
  parsers: [
84
84
  new SlashDateFormatParser(littleEndian),
85
- new ENTimeUnitWithinFormatParser(),
85
+ new ENTimeUnitWithinFormatParser(strictMode),
86
86
  new ENMonthNameLittleEndianParser(),
87
87
  new ENMonthNameMiddleEndianParser(),
88
88
  new ENWeekdayParser(),
@@ -1,11 +1,11 @@
1
1
  import { ParsingContext } from "../../../chrono";
2
- import { parseTimeUnits, TIME_UNITS_PATTERN } from "../constants";
2
+ import { parseTimeUnits, TIME_UNITS_NO_ABBR_PATTERN, TIME_UNITS_PATTERN } from "../constants";
3
3
  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(`(${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");
7
+ const PATTERN = new RegExp(`(${TIME_UNITS_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
8
+ const STRICT_PATTERN = new RegExp(`(${TIME_UNITS_NO_ABBR_PATTERN})\\s{0,5}(?:ago|before|earlier)(?=\\W|$)`, "i");
9
9
 
10
10
  export default class ENTimeUnitAgoFormatParser extends AbstractParserWithWordBoundaryChecking {
11
11
  constructor(private strictMode: boolean) {
@@ -1,14 +1,22 @@
1
- import { TIME_UNITS_PATTERN, parseTimeUnits } from "../constants";
1
+ import { TIME_UNITS_PATTERN, parseTimeUnits, TIME_UNITS_NO_ABBR_PATTERN } from "../constants";
2
2
  import { ParsingContext } from "../../../chrono";
3
3
  import { ParsingComponents } from "../../../results";
4
4
  import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
5
5
  import { reverseTimeUnits } from "../../../utils/timeunits";
6
6
 
7
7
  const PATTERN = new RegExp(`(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_PATTERN})(?=\\W|$)`, "i");
8
+ const PATTERN_NO_ABBR = new RegExp(
9
+ `(this|last|past|next|after|\\+|-)\\s*(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`,
10
+ "i"
11
+ );
8
12
 
9
13
  export default class ENTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundaryChecking {
14
+ constructor(private allowAbbreviations: boolean = true) {
15
+ super();
16
+ }
17
+
10
18
  innerPattern(): RegExp {
11
- return PATTERN;
19
+ return this.allowAbbreviations ? PATTERN : PATTERN_NO_ABBR;
12
20
  }
13
21
 
14
22
  innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingComponents {
@@ -1,5 +1,5 @@
1
1
  import { ParsingContext } from "../../../chrono";
2
- import { parseTimeUnits, TIME_UNITS_PATTERN } from "../constants";
2
+ import { parseTimeUnits, TIME_UNITS_NO_ABBR_PATTERN, TIME_UNITS_PATTERN } from "../constants";
3
3
  import { ParsingComponents } from "../../../results";
4
4
  import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
5
5
 
@@ -8,7 +8,10 @@ const PATTERN = new RegExp(
8
8
  "i"
9
9
  );
10
10
 
11
- const STRICT_PATTERN = new RegExp("" + "(" + TIME_UNITS_PATTERN + ")" + "(later|from now)" + "(?=(?:\\W|$))", "i");
11
+ const STRICT_PATTERN = new RegExp(
12
+ "" + "(" + TIME_UNITS_NO_ABBR_PATTERN + ")" + "(later|from now)" + "(?=(?:\\W|$))",
13
+ "i"
14
+ );
12
15
  const GROUP_NUM_TIMEUNITS = 1;
13
16
 
14
17
  export default class ENTimeUnitLaterFormatParser extends AbstractParserWithWordBoundaryChecking {
@@ -1,21 +1,34 @@
1
- import { TIME_UNITS_PATTERN, parseTimeUnits } from "../constants";
1
+ import { TIME_UNITS_PATTERN, parseTimeUnits, TIME_UNITS_NO_ABBR_PATTERN } from "../constants";
2
2
  import { ParsingContext } from "../../../chrono";
3
3
  import { ParsingComponents } from "../../../results";
4
4
  import { AbstractParserWithWordBoundaryChecking } from "../../../common/parsers/AbstractParserWithWordBoundary";
5
5
 
6
+ const PATTERN_WITHOUT_PREFIX = new RegExp(
7
+ `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_PATTERN})(?=\\W|$)`,
8
+ "i"
9
+ );
10
+
6
11
  const PATTERN_WITH_PREFIX = new RegExp(
7
12
  `(?:within|in|for)\\s*` +
8
13
  `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_PATTERN})(?=\\W|$)`,
9
14
  "i"
10
15
  );
11
16
 
12
- const PATTERN_WITHOUT_PREFIX = new RegExp(
13
- `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_PATTERN})(?=\\W|$)`,
17
+ const PATTERN_WITH_PREFIX_STRICT = new RegExp(
18
+ `(?:within|in|for)\\s*` +
19
+ `(?:(?:about|around|roughly|approximately|just)\\s*(?:~\\s*)?)?(${TIME_UNITS_NO_ABBR_PATTERN})(?=\\W|$)`,
14
20
  "i"
15
21
  );
16
22
 
17
23
  export default class ENTimeUnitWithinFormatParser extends AbstractParserWithWordBoundaryChecking {
24
+ constructor(private strictMode: boolean) {
25
+ super();
26
+ }
27
+
18
28
  innerPattern(context: ParsingContext): RegExp {
29
+ if (this.strictMode) {
30
+ return PATTERN_WITH_PREFIX_STRICT;
31
+ }
19
32
  return context.option.forwardDate ? PATTERN_WITHOUT_PREFIX : PATTERN_WITH_PREFIX;
20
33
  }
21
34
 
@@ -219,6 +219,9 @@ export const TIME_UNIT_DICTIONARY: { [word: string]: OpUnitType | QUnitType } =
219
219
  минуток: "minute",
220
220
  минутки: "minute",
221
221
  минутку: "minute",
222
+ минуточек: "minute",
223
+ минуточки: "minute",
224
+ минуточку: "minute",
222
225
  час: "hour",
223
226
  часов: "hour",
224
227
  часа: "hour",
@@ -284,7 +287,7 @@ export function parseNumberPattern(match: string): number {
284
287
 
285
288
  export const ORDINAL_NUMBER_PATTERN = `(?:${matchAnyPattern(ORDINAL_WORD_DICTIONARY)}|[0-9]{1,2}(?:го|ого|е|ое)?)`;
286
289
  export function parseOrdinalNumberPattern(match: string): number {
287
- let num = match.toLowerCase();
290
+ const num = match.toLowerCase();
288
291
  if (ORDINAL_WORD_DICTIONARY[num] !== undefined) {
289
292
  return ORDINAL_WORD_DICTIONARY[num];
290
293
  }
package/src/results.ts CHANGED
@@ -16,7 +16,7 @@ export class ReferenceWithTimezone {
16
16
  this.instant = input;
17
17
  } else {
18
18
  this.instant = input.instant ?? new Date();
19
- this.timezoneOffset = toTimezoneOffset(input.timezone);
19
+ this.timezoneOffset = toTimezoneOffset(input.timezone, this.instant);
20
20
  }
21
21
  }
22
22
 
@@ -138,8 +138,8 @@ export class ParsingComponents implements ParsedComponents {
138
138
  return this.isCertain("weekday") && !this.isCertain("day") && !this.isCertain("month");
139
139
  }
140
140
 
141
- isOnlyDayMonthComponent(): boolean {
142
- return this.isCertain("day") && this.isCertain("month") && !this.isCertain("year");
141
+ isDateWithUnknownYear(): boolean {
142
+ return this.isCertain("month") && !this.isCertain("year");
143
143
  }
144
144
 
145
145
  isValidDate(): boolean {