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.
- package/README.md +1 -1
- package/dist/common/refiners/ExtractTimezoneAbbrRefiner.js +6 -1
- package/dist/locales/de/constants.d.ts +3 -3
- package/dist/locales/de/constants.js +38 -20
- package/dist/locales/de/index.js +4 -0
- package/dist/locales/de/parsers/DESpecificTimeExpressionParser.d.ts +7 -0
- package/dist/locales/de/parsers/DESpecificTimeExpressionParser.js +114 -0
- package/dist/locales/de/parsers/DETimeExpressionParser.d.ts +0 -1
- package/dist/locales/de/parsers/DETimeExpressionParser.js +3 -21
- package/dist/locales/de/parsers/DETimeUnitRelativeFormatParser.d.ts +8 -0
- package/dist/locales/de/parsers/DETimeUnitRelativeFormatParser.js +34 -0
- package/dist/locales/en/constants.d.ts +2 -2
- package/dist/locales/en/constants.js +3 -0
- 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/locales/zh/hans/constants.d.ts +27 -0
- package/dist/locales/zh/hans/constants.js +51 -0
- package/dist/locales/zh/hans/index.d.ts +9 -0
- package/dist/locales/zh/hans/index.js +49 -0
- package/dist/locales/zh/hans/parsers/ZHHansCasualDateParser.d.ts +7 -0
- package/dist/locales/zh/hans/parsers/ZHHansCasualDateParser.js +138 -0
- package/dist/locales/zh/hans/parsers/ZHHansDateParser.d.ts +6 -0
- package/dist/locales/zh/hans/parsers/ZHHansDateParser.js +72 -0
- package/dist/locales/zh/hans/parsers/ZHHansDeadlineFormatParser.d.ts +6 -0
- package/dist/locales/zh/hans/parsers/ZHHansDeadlineFormatParser.js +78 -0
- package/dist/locales/zh/hans/parsers/ZHHansRelationWeekdayParser.d.ts +7 -0
- package/dist/locales/zh/hans/parsers/ZHHansRelationWeekdayParser.js +70 -0
- package/dist/locales/zh/hans/parsers/ZHHansTimeExpressionParser.d.ts +6 -0
- package/dist/locales/zh/hans/parsers/ZHHansTimeExpressionParser.js +438 -0
- package/dist/locales/zh/hans/parsers/ZHHansWeekdayParser.d.ts +7 -0
- package/dist/locales/zh/hans/parsers/ZHHansWeekdayParser.js +46 -0
- package/dist/locales/zh/hans/refiners/ZHHansMergeDateRangeRefiner.d.ts +4 -0
- package/dist/locales/zh/hans/refiners/ZHHansMergeDateRangeRefiner.js +12 -0
- package/dist/locales/zh/hans/refiners/ZHHansMergeDateTimeRefiner.d.ts +4 -0
- package/dist/locales/zh/hans/refiners/ZHHansMergeDateTimeRefiner.js +12 -0
- package/dist/locales/zh/hant/index.d.ts +9 -0
- package/dist/locales/zh/hant/index.js +49 -0
- package/dist/locales/zh/index.d.ts +2 -9
- package/dist/locales/zh/index.js +13 -46
- package/dist/results.d.ts +1 -1
- package/dist/results.js +1 -2
- package/dist/timezone.d.ts +1 -1
- package/dist/timezone.js +3 -3
- package/package.json +1 -1
- package/src/common/refiners/ExtractTimezoneAbbrRefiner.ts +11 -1
- package/src/locales/de/constants.ts +42 -22
- package/src/locales/de/index.ts +4 -0
- package/src/locales/de/parsers/DESpecificTimeExpressionParser.ts +150 -0
- package/src/locales/de/parsers/DETimeExpressionParser.ts +4 -23
- package/src/locales/de/parsers/DETimeUnitRelativeFormatParser.ts +42 -0
- package/src/locales/en/constants.ts +5 -2
- package/src/locales/en/index.ts +2 -1
- package/src/locales/en/parsers/ENMonthNameLittleEndianParser.ts +0 -2
- 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/locales/zh/hans/constants.ts +52 -0
- package/src/locales/zh/hans/index.ts +62 -0
- package/src/locales/zh/hans/parsers/ZHHansCasualDateParser.ts +128 -0
- package/src/locales/zh/hans/parsers/ZHHansDateParser.ts +75 -0
- package/src/locales/zh/hans/parsers/ZHHansDeadlineFormatParser.ts +81 -0
- package/src/locales/zh/hans/parsers/ZHHansRelationWeekdayParser.ts +69 -0
- package/src/locales/zh/hans/parsers/ZHHansTimeExpressionParser.ts +424 -0
- package/src/locales/zh/hans/parsers/ZHHansWeekdayParser.ts +46 -0
- package/src/locales/zh/hans/refiners/ZHHansMergeDateRangeRefiner.ts +7 -0
- package/src/locales/zh/hans/refiners/ZHHansMergeDateTimeRefiner.ts +7 -0
- package/src/locales/zh/hant/index.ts +63 -0
- package/src/locales/zh/index.ts +2 -63
- package/src/results.ts +2 -3
- package/src/timezone.ts +4 -4
- package/test/de/de_month_name_little_endian.test.ts +12 -0
- package/test/de/de_time_exp.test.ts +59 -0
- package/test/de/de_time_units_casual_relative.test.ts +108 -0
- package/test/de/de_year.test.ts +147 -0
- package/test/en/en_merging_relative_dates.test.ts +51 -0
- package/test/en/en_month.test.ts +13 -0
- package/test/en/en_relative.test.ts +61 -2
- package/test/en/en_time_units_ago.test.ts +3 -3
- package/test/en/en_time_units_casual_relative.test.ts +16 -0
- package/test/en/en_time_units_within.test.ts +2 -2
- package/test/en/en_timezone_exp.test.ts +3 -3
- package/test/fr/fr_time_units_ago.test.ts +3 -3
- package/test/fr/fr_time_units_within.test.ts +2 -2
- package/test/nl/nl_relative.test.ts +2 -2
- package/test/nl/nl_time_units_ago.test.ts +3 -3
- package/test/system_timezone.test.ts +22 -0
- package/test/zh/zh_hans_casual.test.ts +228 -0
- package/test/zh/zh_hans_date.test.ts +102 -0
- package/test/zh/zh_hans_deadline.test.ts +136 -0
- package/test/zh/zh_hans_time_exp.test.ts +182 -0
- package/test/zh/zh_hans_weekday.test.ts +132 -0
- package/test/zh/zh_hant_date.test.ts +0 -1
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import { ParsingContext } from "../../../../chrono";
|
|
3
|
+
import { AbstractParserWithWordBoundaryChecking } from "../../../../common/parsers/AbstractParserWithWordBoundary";
|
|
4
|
+
import { NUMBER, zhStringToNumber } from "../constants";
|
|
5
|
+
|
|
6
|
+
const FIRST_REG_PATTERN = new RegExp(
|
|
7
|
+
"(?:从|自)?" +
|
|
8
|
+
"(?:" +
|
|
9
|
+
"(今|明|前|大前|后|大后|昨)(早|朝|晚)|" +
|
|
10
|
+
"(上(?:午)|早(?:上)|下(?:午)|晚(?:上)|夜(?:晚)?|中(?:午)|凌(?:晨))|" +
|
|
11
|
+
"(今|明|前|大前|后|大后|昨)(?:日|天)" +
|
|
12
|
+
"(?:[\\s,,]*)" +
|
|
13
|
+
"(?:(上(?:午)|早(?:上)|下(?:午)|晚(?:上)|夜(?:晚)?|中(?:午)|凌(?:晨)))?" +
|
|
14
|
+
")?" +
|
|
15
|
+
"(?:[\\s,,]*)" +
|
|
16
|
+
"(?:(\\d+|[" +
|
|
17
|
+
Object.keys(NUMBER).join("") +
|
|
18
|
+
"]+)(?:\\s*)(?:点|时|:|:)" +
|
|
19
|
+
"(?:\\s*)" +
|
|
20
|
+
"(\\d+|半|正|整|[" +
|
|
21
|
+
Object.keys(NUMBER).join("") +
|
|
22
|
+
"]+)?(?:\\s*)(?:分|:|:)?" +
|
|
23
|
+
"(?:\\s*)" +
|
|
24
|
+
"(\\d+|[" +
|
|
25
|
+
Object.keys(NUMBER).join("") +
|
|
26
|
+
"]+)?(?:\\s*)(?:秒)?)" +
|
|
27
|
+
"(?:\\s*(A.M.|P.M.|AM?|PM?))?",
|
|
28
|
+
"i"
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const SECOND_REG_PATTERN = new RegExp(
|
|
32
|
+
"(?:^\\s*(?:到|至|\\-|\\–|\\~|\\〜)\\s*)" +
|
|
33
|
+
"(?:" +
|
|
34
|
+
"(今|明|前|大前|后|大后|昨)(早|朝|晚)|" +
|
|
35
|
+
"(上(?:午)|早(?:上)|下(?:午)|晚(?:上)|夜(?:晚)?|中(?:午)|凌(?:晨))|" +
|
|
36
|
+
"(今|明|前|大前|后|大后|昨)(?:日|天)" +
|
|
37
|
+
"(?:[\\s,,]*)" +
|
|
38
|
+
"(?:(上(?:午)|早(?:上)|下(?:午)|晚(?:上)|夜(?:晚)?|中(?:午)|凌(?:晨)))?" +
|
|
39
|
+
")?" +
|
|
40
|
+
"(?:[\\s,,]*)" +
|
|
41
|
+
"(?:(\\d+|[" +
|
|
42
|
+
Object.keys(NUMBER).join("") +
|
|
43
|
+
"]+)(?:\\s*)(?:点|时|:|:)" +
|
|
44
|
+
"(?:\\s*)" +
|
|
45
|
+
"(\\d+|半|正|整|[" +
|
|
46
|
+
Object.keys(NUMBER).join("") +
|
|
47
|
+
"]+)?(?:\\s*)(?:分|:|:)?" +
|
|
48
|
+
"(?:\\s*)" +
|
|
49
|
+
"(\\d+|[" +
|
|
50
|
+
Object.keys(NUMBER).join("") +
|
|
51
|
+
"]+)?(?:\\s*)(?:秒)?)" +
|
|
52
|
+
"(?:\\s*(A.M.|P.M.|AM?|PM?))?",
|
|
53
|
+
"i"
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const DAY_GROUP_1 = 1;
|
|
57
|
+
const ZH_AM_PM_HOUR_GROUP_1 = 2;
|
|
58
|
+
const ZH_AM_PM_HOUR_GROUP_2 = 3;
|
|
59
|
+
const DAY_GROUP_3 = 4;
|
|
60
|
+
const ZH_AM_PM_HOUR_GROUP_3 = 5;
|
|
61
|
+
const HOUR_GROUP = 6;
|
|
62
|
+
const MINUTE_GROUP = 7;
|
|
63
|
+
const SECOND_GROUP = 8;
|
|
64
|
+
const AM_PM_HOUR_GROUP = 9;
|
|
65
|
+
|
|
66
|
+
export default class ZHHansTimeExpressionParser extends AbstractParserWithWordBoundaryChecking {
|
|
67
|
+
innerPattern(): RegExp {
|
|
68
|
+
return FIRST_REG_PATTERN;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
innerExtract(context: ParsingContext, match: RegExpMatchArray) {
|
|
72
|
+
// This pattern can be overlaped Ex. [12] AM, 1[2] AM
|
|
73
|
+
if (match.index > 0 && context.text[match.index - 1].match(/\w/)) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const refMoment = dayjs(context.refDate);
|
|
78
|
+
const result = context.createParsingResult(match.index, match[0]);
|
|
79
|
+
const startMoment = refMoment.clone();
|
|
80
|
+
|
|
81
|
+
// ----- Day
|
|
82
|
+
if (match[DAY_GROUP_1]) {
|
|
83
|
+
const day1 = match[DAY_GROUP_1];
|
|
84
|
+
if (day1 == "明") {
|
|
85
|
+
// Check not "Tomorrow" on late night
|
|
86
|
+
if (refMoment.hour() > 1) {
|
|
87
|
+
startMoment.add(1, "day");
|
|
88
|
+
}
|
|
89
|
+
} else if (day1 == "昨") {
|
|
90
|
+
startMoment.add(-1, "day");
|
|
91
|
+
} else if (day1 == "前") {
|
|
92
|
+
startMoment.add(-2, "day");
|
|
93
|
+
} else if (day1 == "大前") {
|
|
94
|
+
startMoment.add(-3, "day");
|
|
95
|
+
} else if (day1 == "后") {
|
|
96
|
+
startMoment.add(2, "day");
|
|
97
|
+
} else if (day1 == "大后") {
|
|
98
|
+
startMoment.add(3, "day");
|
|
99
|
+
}
|
|
100
|
+
result.start.assign("day", startMoment.date());
|
|
101
|
+
result.start.assign("month", startMoment.month() + 1);
|
|
102
|
+
result.start.assign("year", startMoment.year());
|
|
103
|
+
} else if (match[DAY_GROUP_3]) {
|
|
104
|
+
const day3 = match[DAY_GROUP_3];
|
|
105
|
+
if (day3 == "明") {
|
|
106
|
+
startMoment.add(1, "day");
|
|
107
|
+
} else if (day3 == "昨") {
|
|
108
|
+
startMoment.add(-1, "day");
|
|
109
|
+
} else if (day3 == "前") {
|
|
110
|
+
startMoment.add(-2, "day");
|
|
111
|
+
} else if (day3 == "大前") {
|
|
112
|
+
startMoment.add(-3, "day");
|
|
113
|
+
} else if (day3 == "后") {
|
|
114
|
+
startMoment.add(2, "day");
|
|
115
|
+
} else if (day3 == "大后") {
|
|
116
|
+
startMoment.add(3, "day");
|
|
117
|
+
}
|
|
118
|
+
result.start.assign("day", startMoment.date());
|
|
119
|
+
result.start.assign("month", startMoment.month() + 1);
|
|
120
|
+
result.start.assign("year", startMoment.year());
|
|
121
|
+
} else {
|
|
122
|
+
result.start.imply("day", startMoment.date());
|
|
123
|
+
result.start.imply("month", startMoment.month() + 1);
|
|
124
|
+
result.start.imply("year", startMoment.year());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let hour = 0;
|
|
128
|
+
let minute = 0;
|
|
129
|
+
let meridiem = -1;
|
|
130
|
+
|
|
131
|
+
// ----- Second
|
|
132
|
+
if (match[SECOND_GROUP]) {
|
|
133
|
+
let second = parseInt(match[SECOND_GROUP]);
|
|
134
|
+
if (isNaN(second)) {
|
|
135
|
+
second = zhStringToNumber(match[SECOND_GROUP]);
|
|
136
|
+
}
|
|
137
|
+
if (second >= 60) return null;
|
|
138
|
+
result.start.assign("second", second);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
hour = parseInt(match[HOUR_GROUP]);
|
|
142
|
+
if (isNaN(hour)) {
|
|
143
|
+
hour = zhStringToNumber(match[HOUR_GROUP]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ----- Minutes
|
|
147
|
+
if (match[MINUTE_GROUP]) {
|
|
148
|
+
if (match[MINUTE_GROUP] == "半") {
|
|
149
|
+
minute = 30;
|
|
150
|
+
} else if (match[MINUTE_GROUP] == "正" || match[MINUTE_GROUP] == "整") {
|
|
151
|
+
minute = 0;
|
|
152
|
+
} else {
|
|
153
|
+
minute = parseInt(match[MINUTE_GROUP]);
|
|
154
|
+
if (isNaN(minute)) {
|
|
155
|
+
minute = zhStringToNumber(match[MINUTE_GROUP]);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} else if (hour > 100) {
|
|
159
|
+
minute = hour % 100;
|
|
160
|
+
hour = Math.floor(hour / 100);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (minute >= 60) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (hour > 24) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
if (hour >= 12) {
|
|
171
|
+
meridiem = 1;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ----- AM & PM
|
|
175
|
+
if (match[AM_PM_HOUR_GROUP]) {
|
|
176
|
+
if (hour > 12) return null;
|
|
177
|
+
const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase();
|
|
178
|
+
if (ampm == "a") {
|
|
179
|
+
meridiem = 0;
|
|
180
|
+
if (hour == 12) hour = 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (ampm == "p") {
|
|
184
|
+
meridiem = 1;
|
|
185
|
+
if (hour != 12) hour += 12;
|
|
186
|
+
}
|
|
187
|
+
} else if (match[ZH_AM_PM_HOUR_GROUP_1]) {
|
|
188
|
+
const zhAMPMString1 = match[ZH_AM_PM_HOUR_GROUP_1];
|
|
189
|
+
const zhAMPM1 = zhAMPMString1[0];
|
|
190
|
+
if (zhAMPM1 == "早") {
|
|
191
|
+
meridiem = 0;
|
|
192
|
+
if (hour == 12) hour = 0;
|
|
193
|
+
} else if (zhAMPM1 == "晚") {
|
|
194
|
+
meridiem = 1;
|
|
195
|
+
if (hour != 12) hour += 12;
|
|
196
|
+
}
|
|
197
|
+
} else if (match[ZH_AM_PM_HOUR_GROUP_2]) {
|
|
198
|
+
const zhAMPMString2 = match[ZH_AM_PM_HOUR_GROUP_2];
|
|
199
|
+
const zhAMPM2 = zhAMPMString2[0];
|
|
200
|
+
if (zhAMPM2 == "上" || zhAMPM2 == "早" || zhAMPM2 == "凌") {
|
|
201
|
+
meridiem = 0;
|
|
202
|
+
if (hour == 12) hour = 0;
|
|
203
|
+
} else if (zhAMPM2 == "下" || zhAMPM2 == "晚") {
|
|
204
|
+
meridiem = 1;
|
|
205
|
+
if (hour != 12) hour += 12;
|
|
206
|
+
}
|
|
207
|
+
} else if (match[ZH_AM_PM_HOUR_GROUP_3]) {
|
|
208
|
+
const zhAMPMString3 = match[ZH_AM_PM_HOUR_GROUP_3];
|
|
209
|
+
const zhAMPM3 = zhAMPMString3[0];
|
|
210
|
+
if (zhAMPM3 == "上" || zhAMPM3 == "早" || zhAMPM3 == "凌") {
|
|
211
|
+
meridiem = 0;
|
|
212
|
+
if (hour == 12) hour = 0;
|
|
213
|
+
} else if (zhAMPM3 == "下" || zhAMPM3 == "晚") {
|
|
214
|
+
meridiem = 1;
|
|
215
|
+
if (hour != 12) hour += 12;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
result.start.assign("hour", hour);
|
|
220
|
+
result.start.assign("minute", minute);
|
|
221
|
+
|
|
222
|
+
if (meridiem >= 0) {
|
|
223
|
+
result.start.assign("meridiem", meridiem);
|
|
224
|
+
} else {
|
|
225
|
+
if (hour < 12) {
|
|
226
|
+
result.start.imply("meridiem", 0);
|
|
227
|
+
} else {
|
|
228
|
+
result.start.imply("meridiem", 1);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ==============================================================
|
|
233
|
+
// Extracting the 'to' chunk
|
|
234
|
+
// ==============================================================
|
|
235
|
+
|
|
236
|
+
match = SECOND_REG_PATTERN.exec(context.text.substring(result.index + result.text.length));
|
|
237
|
+
if (!match) {
|
|
238
|
+
// Not accept number only result
|
|
239
|
+
if (result.text.match(/^\d+$/)) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const endMoment = startMoment.clone();
|
|
246
|
+
result.end = context.createParsingComponents();
|
|
247
|
+
|
|
248
|
+
// ----- Day
|
|
249
|
+
if (match[DAY_GROUP_1]) {
|
|
250
|
+
const day1 = match[DAY_GROUP_1];
|
|
251
|
+
if (day1 == "明") {
|
|
252
|
+
// Check not "Tomorrow" on late night
|
|
253
|
+
if (refMoment.hour() > 1) {
|
|
254
|
+
endMoment.add(1, "day");
|
|
255
|
+
}
|
|
256
|
+
} else if (day1 == "昨") {
|
|
257
|
+
endMoment.add(-1, "day");
|
|
258
|
+
} else if (day1 == "前") {
|
|
259
|
+
endMoment.add(-2, "day");
|
|
260
|
+
} else if (day1 == "大前") {
|
|
261
|
+
endMoment.add(-3, "day");
|
|
262
|
+
} else if (day1 == "后") {
|
|
263
|
+
endMoment.add(2, "day");
|
|
264
|
+
} else if (day1 == "大后") {
|
|
265
|
+
endMoment.add(3, "day");
|
|
266
|
+
}
|
|
267
|
+
result.end.assign("day", endMoment.date());
|
|
268
|
+
result.end.assign("month", endMoment.month() + 1);
|
|
269
|
+
result.end.assign("year", endMoment.year());
|
|
270
|
+
} else if (match[DAY_GROUP_3]) {
|
|
271
|
+
const day3 = match[DAY_GROUP_3];
|
|
272
|
+
if (day3 == "明") {
|
|
273
|
+
endMoment.add(1, "day");
|
|
274
|
+
} else if (day3 == "昨") {
|
|
275
|
+
endMoment.add(-1, "day");
|
|
276
|
+
} else if (day3 == "前") {
|
|
277
|
+
endMoment.add(-2, "day");
|
|
278
|
+
} else if (day3 == "大前") {
|
|
279
|
+
endMoment.add(-3, "day");
|
|
280
|
+
} else if (day3 == "后") {
|
|
281
|
+
endMoment.add(2, "day");
|
|
282
|
+
} else if (day3 == "大后") {
|
|
283
|
+
endMoment.add(3, "day");
|
|
284
|
+
}
|
|
285
|
+
result.end.assign("day", endMoment.date());
|
|
286
|
+
result.end.assign("month", endMoment.month() + 1);
|
|
287
|
+
result.end.assign("year", endMoment.year());
|
|
288
|
+
} else {
|
|
289
|
+
result.end.imply("day", endMoment.date());
|
|
290
|
+
result.end.imply("month", endMoment.month() + 1);
|
|
291
|
+
result.end.imply("year", endMoment.year());
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
hour = 0;
|
|
295
|
+
minute = 0;
|
|
296
|
+
meridiem = -1;
|
|
297
|
+
|
|
298
|
+
// ----- Second
|
|
299
|
+
if (match[SECOND_GROUP]) {
|
|
300
|
+
let second = parseInt(match[SECOND_GROUP]);
|
|
301
|
+
if (isNaN(second)) {
|
|
302
|
+
second = zhStringToNumber(match[SECOND_GROUP]);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (second >= 60) return null;
|
|
306
|
+
result.end.assign("second", second);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
hour = parseInt(match[HOUR_GROUP]);
|
|
310
|
+
if (isNaN(hour)) {
|
|
311
|
+
hour = zhStringToNumber(match[HOUR_GROUP]);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ----- Minutes
|
|
315
|
+
if (match[MINUTE_GROUP]) {
|
|
316
|
+
if (match[MINUTE_GROUP] == "半") {
|
|
317
|
+
minute = 30;
|
|
318
|
+
} else if (match[MINUTE_GROUP] == "正" || match[MINUTE_GROUP] == "整") {
|
|
319
|
+
minute = 0;
|
|
320
|
+
} else {
|
|
321
|
+
minute = parseInt(match[MINUTE_GROUP]);
|
|
322
|
+
if (isNaN(minute)) {
|
|
323
|
+
minute = zhStringToNumber(match[MINUTE_GROUP]);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
} else if (hour > 100) {
|
|
327
|
+
minute = hour % 100;
|
|
328
|
+
hour = Math.floor(hour / 100);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (minute >= 60) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (hour > 24) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
if (hour >= 12) {
|
|
339
|
+
meridiem = 1;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ----- AM & PM
|
|
343
|
+
if (match[AM_PM_HOUR_GROUP]) {
|
|
344
|
+
if (hour > 12) return null;
|
|
345
|
+
const ampm = match[AM_PM_HOUR_GROUP][0].toLowerCase();
|
|
346
|
+
if (ampm == "a") {
|
|
347
|
+
meridiem = 0;
|
|
348
|
+
if (hour == 12) hour = 0;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (ampm == "p") {
|
|
352
|
+
meridiem = 1;
|
|
353
|
+
if (hour != 12) hour += 12;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!result.start.isCertain("meridiem")) {
|
|
357
|
+
if (meridiem == 0) {
|
|
358
|
+
result.start.imply("meridiem", 0);
|
|
359
|
+
|
|
360
|
+
if (result.start.get("hour") == 12) {
|
|
361
|
+
result.start.assign("hour", 0);
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
result.start.imply("meridiem", 1);
|
|
365
|
+
|
|
366
|
+
if (result.start.get("hour") != 12) {
|
|
367
|
+
result.start.assign("hour", result.start.get("hour") + 12);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
} else if (match[ZH_AM_PM_HOUR_GROUP_1]) {
|
|
372
|
+
const zhAMPMString1 = match[ZH_AM_PM_HOUR_GROUP_1];
|
|
373
|
+
const zhAMPM1 = zhAMPMString1[0];
|
|
374
|
+
if (zhAMPM1 == "早") {
|
|
375
|
+
meridiem = 0;
|
|
376
|
+
if (hour == 12) hour = 0;
|
|
377
|
+
} else if (zhAMPM1 == "晚") {
|
|
378
|
+
meridiem = 1;
|
|
379
|
+
if (hour != 12) hour += 12;
|
|
380
|
+
}
|
|
381
|
+
} else if (match[ZH_AM_PM_HOUR_GROUP_2]) {
|
|
382
|
+
const zhAMPMString2 = match[ZH_AM_PM_HOUR_GROUP_2];
|
|
383
|
+
const zhAMPM2 = zhAMPMString2[0];
|
|
384
|
+
if (zhAMPM2 == "上" || zhAMPM2 == "早" || zhAMPM2 == "凌") {
|
|
385
|
+
meridiem = 0;
|
|
386
|
+
if (hour == 12) hour = 0;
|
|
387
|
+
} else if (zhAMPM2 == "下" || zhAMPM2 == "晚") {
|
|
388
|
+
meridiem = 1;
|
|
389
|
+
if (hour != 12) hour += 12;
|
|
390
|
+
}
|
|
391
|
+
} else if (match[ZH_AM_PM_HOUR_GROUP_3]) {
|
|
392
|
+
const zhAMPMString3 = match[ZH_AM_PM_HOUR_GROUP_3];
|
|
393
|
+
const zhAMPM3 = zhAMPMString3[0];
|
|
394
|
+
if (zhAMPM3 == "上" || zhAMPM3 == "早" || zhAMPM3 == "凌") {
|
|
395
|
+
meridiem = 0;
|
|
396
|
+
if (hour == 12) hour = 0;
|
|
397
|
+
} else if (zhAMPM3 == "下" || zhAMPM3 == "晚") {
|
|
398
|
+
meridiem = 1;
|
|
399
|
+
if (hour != 12) hour += 12;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
result.text = result.text + match[0];
|
|
404
|
+
result.end.assign("hour", hour);
|
|
405
|
+
result.end.assign("minute", minute);
|
|
406
|
+
if (meridiem >= 0) {
|
|
407
|
+
result.end.assign("meridiem", meridiem);
|
|
408
|
+
} else {
|
|
409
|
+
const startAtPM = result.start.isCertain("meridiem") && result.start.get("meridiem") == 1;
|
|
410
|
+
if (startAtPM && result.start.get("hour") > hour) {
|
|
411
|
+
// 10pm - 1 (am)
|
|
412
|
+
result.end.imply("meridiem", 0);
|
|
413
|
+
} else if (hour > 12) {
|
|
414
|
+
result.end.imply("meridiem", 1);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (result.end.date().getTime() < result.start.date().getTime()) {
|
|
419
|
+
result.end.imply("day", result.end.get("day") + 1);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import { ParsingContext } from "../../../../chrono";
|
|
3
|
+
import { AbstractParserWithWordBoundaryChecking } from "../../../../common/parsers/AbstractParserWithWordBoundary";
|
|
4
|
+
import { ParsingResult } from "../../../../results";
|
|
5
|
+
import { WEEKDAY_OFFSET } from "../constants";
|
|
6
|
+
|
|
7
|
+
const PATTERN = new RegExp("(?:星期|礼拜|周)(?<weekday>" + Object.keys(WEEKDAY_OFFSET).join("|") + ")");
|
|
8
|
+
|
|
9
|
+
export default class ZHHansWeekdayParser extends AbstractParserWithWordBoundaryChecking {
|
|
10
|
+
innerPattern(): RegExp {
|
|
11
|
+
return PATTERN;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
innerExtract(context: ParsingContext, match: RegExpMatchArray): ParsingResult {
|
|
15
|
+
const result = context.createParsingResult(match.index, match[0]);
|
|
16
|
+
|
|
17
|
+
const dayOfWeek = match.groups.weekday;
|
|
18
|
+
const offset = WEEKDAY_OFFSET[dayOfWeek];
|
|
19
|
+
if (offset === undefined) return null;
|
|
20
|
+
|
|
21
|
+
let startMoment = dayjs(context.refDate);
|
|
22
|
+
const startMomentFixed = false;
|
|
23
|
+
const refOffset = startMoment.day();
|
|
24
|
+
|
|
25
|
+
if (Math.abs(offset - 7 - refOffset) < Math.abs(offset - refOffset)) {
|
|
26
|
+
startMoment = startMoment.day(offset - 7);
|
|
27
|
+
} else if (Math.abs(offset + 7 - refOffset) < Math.abs(offset - refOffset)) {
|
|
28
|
+
startMoment = startMoment.day(offset + 7);
|
|
29
|
+
} else {
|
|
30
|
+
startMoment = startMoment.day(offset);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
result.start.assign("weekday", offset);
|
|
34
|
+
if (startMomentFixed) {
|
|
35
|
+
result.start.assign("day", startMoment.date());
|
|
36
|
+
result.start.assign("month", startMoment.month() + 1);
|
|
37
|
+
result.start.assign("year", startMoment.year());
|
|
38
|
+
} else {
|
|
39
|
+
result.start.imply("day", startMoment.date());
|
|
40
|
+
result.start.imply("month", startMoment.month() + 1);
|
|
41
|
+
result.start.imply("year", startMoment.year());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import AbstractMergeDateRangeRefiner from "../../../../common/refiners/AbstractMergeDateRangeRefiner";
|
|
2
|
+
|
|
3
|
+
export default class ZHHansMergeDateRangeRefiner extends AbstractMergeDateRangeRefiner {
|
|
4
|
+
patternBetween(): RegExp {
|
|
5
|
+
return /^\s*(至|到|-|~|~|-|ー)\s*$/i;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrono components for zh support
|
|
3
|
+
* TODO: Complete general zh support (current support only zh-Hant)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Chrono, Configuration } from "../../../chrono";
|
|
7
|
+
import ExtractTimezoneOffsetRefiner from "../../../common/refiners/ExtractTimezoneOffsetRefiner";
|
|
8
|
+
import { includeCommonConfiguration } from "../../../configurations";
|
|
9
|
+
import { ParsedResult, ParsingOption } from "../../../index";
|
|
10
|
+
import ZHHantCasualDateParser from "./parsers/ZHHantCasualDateParser";
|
|
11
|
+
import ZHHantDateParser from "./parsers/ZHHantDateParser";
|
|
12
|
+
import ZHHantDeadlineFormatParser from "./parsers/ZHHantDeadlineFormatParser";
|
|
13
|
+
import ZHHantRelationWeekdayParser from "./parsers/ZHHantRelationWeekdayParser";
|
|
14
|
+
import ZHHantTimeExpressionParser from "./parsers/ZHHantTimeExpressionParser";
|
|
15
|
+
import ZHHantWeekdayParser from "./parsers/ZHHantWeekdayParser";
|
|
16
|
+
import ZHHantMergeDateRangeRefiner from "./refiners/ZHHantMergeDateRangeRefiner";
|
|
17
|
+
import ZHHantMergeDateTimeRefiner from "./refiners/ZHHantMergeDateTimeRefiner";
|
|
18
|
+
|
|
19
|
+
// Shortcuts
|
|
20
|
+
export const hant = new Chrono(createCasualConfiguration());
|
|
21
|
+
|
|
22
|
+
export const casual = new Chrono(createCasualConfiguration());
|
|
23
|
+
export const strict = new Chrono(createConfiguration());
|
|
24
|
+
|
|
25
|
+
export function parse(text: string, ref?: Date, option?: ParsingOption): ParsedResult[] {
|
|
26
|
+
return casual.parse(text, ref, option);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function parseDate(text: string, ref?: Date, option?: ParsingOption): Date {
|
|
30
|
+
return casual.parseDate(text, ref, option);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @ignore (to be documented later)
|
|
35
|
+
*/
|
|
36
|
+
export function createCasualConfiguration(): Configuration {
|
|
37
|
+
const option = createConfiguration();
|
|
38
|
+
option.parsers.unshift(new ZHHantCasualDateParser());
|
|
39
|
+
return option;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @ignore (to be documented later)
|
|
44
|
+
*/
|
|
45
|
+
export function createConfiguration(): Configuration {
|
|
46
|
+
const configuration = includeCommonConfiguration({
|
|
47
|
+
parsers: [
|
|
48
|
+
new ZHHantDateParser(),
|
|
49
|
+
new ZHHantRelationWeekdayParser(),
|
|
50
|
+
new ZHHantWeekdayParser(),
|
|
51
|
+
new ZHHantTimeExpressionParser(),
|
|
52
|
+
new ZHHantDeadlineFormatParser(),
|
|
53
|
+
],
|
|
54
|
+
refiners: [new ZHHantMergeDateRangeRefiner(), new ZHHantMergeDateTimeRefiner()],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// REMOVE ExtractTimezoneOffsetRefiner
|
|
58
|
+
configuration.refiners = configuration.refiners.filter(
|
|
59
|
+
(refiner) => !(refiner instanceof ExtractTimezoneOffsetRefiner)
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return configuration;
|
|
63
|
+
}
|
package/src/locales/zh/index.ts
CHANGED
|
@@ -1,63 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* TODO: Complete general zh support (current support only zh-Hant)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Chrono, Configuration } from "../../chrono";
|
|
7
|
-
import ExtractTimezoneOffsetRefiner from "../../common/refiners/ExtractTimezoneOffsetRefiner";
|
|
8
|
-
import { includeCommonConfiguration } from "../../configurations";
|
|
9
|
-
import { ParsedResult, ParsingOption } from "../../index";
|
|
10
|
-
import ZHHantCasualDateParser from "./hant/parsers/ZHHantCasualDateParser";
|
|
11
|
-
import ZHHantDateParser from "./hant/parsers/ZHHantDateParser";
|
|
12
|
-
import ZHHantDeadlineFormatParser from "./hant/parsers/ZHHantDeadlineFormatParser";
|
|
13
|
-
import ZHHantRelationWeekdayParser from "./hant/parsers/ZHHantRelationWeekdayParser";
|
|
14
|
-
import ZHHantTimeExpressionParser from "./hant/parsers/ZHHantTimeExpressionParser";
|
|
15
|
-
import ZHHantWeekdayParser from "./hant/parsers/ZHHantWeekdayParser";
|
|
16
|
-
import ZHHantMergeDateRangeRefiner from "./hant/refiners/ZHHantMergeDateRangeRefiner";
|
|
17
|
-
import ZHHantMergeDateTimeRefiner from "./hant/refiners/ZHHantMergeDateTimeRefiner";
|
|
18
|
-
|
|
19
|
-
// Shortcuts
|
|
20
|
-
export const hant = new Chrono(createCasualConfiguration());
|
|
21
|
-
|
|
22
|
-
export const casual = new Chrono(createCasualConfiguration());
|
|
23
|
-
export const strict = new Chrono(createConfiguration());
|
|
24
|
-
|
|
25
|
-
export function parse(text: string, ref?: Date, option?: ParsingOption): ParsedResult[] {
|
|
26
|
-
return casual.parse(text, ref, option);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function parseDate(text: string, ref?: Date, option?: ParsingOption): Date {
|
|
30
|
-
return casual.parseDate(text, ref, option);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @ignore (to be documented later)
|
|
35
|
-
*/
|
|
36
|
-
export function createCasualConfiguration(): Configuration {
|
|
37
|
-
const option = createConfiguration();
|
|
38
|
-
option.parsers.unshift(new ZHHantCasualDateParser());
|
|
39
|
-
return option;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @ignore (to be documented later)
|
|
44
|
-
*/
|
|
45
|
-
export function createConfiguration(): Configuration {
|
|
46
|
-
const configuration = includeCommonConfiguration({
|
|
47
|
-
parsers: [
|
|
48
|
-
new ZHHantDateParser(),
|
|
49
|
-
new ZHHantRelationWeekdayParser(),
|
|
50
|
-
new ZHHantWeekdayParser(),
|
|
51
|
-
new ZHHantTimeExpressionParser(),
|
|
52
|
-
new ZHHantDeadlineFormatParser(),
|
|
53
|
-
],
|
|
54
|
-
refiners: [new ZHHantMergeDateRangeRefiner(), new ZHHantMergeDateTimeRefiner()],
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// REMOVE ExtractTimezoneOffsetRefiner
|
|
58
|
-
configuration.refiners = configuration.refiners.filter(
|
|
59
|
-
(refiner) => !(refiner instanceof ExtractTimezoneOffsetRefiner)
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
return configuration;
|
|
63
|
-
}
|
|
1
|
+
export * from "./hant";
|
|
2
|
+
export { hans } from "./hans";
|
package/src/results.ts
CHANGED
|
@@ -8,13 +8,12 @@ dayjs.extend(quarterOfYear);
|
|
|
8
8
|
|
|
9
9
|
export class ReferenceWithTimezone {
|
|
10
10
|
readonly instant: Date;
|
|
11
|
-
readonly timezoneOffset?: number;
|
|
11
|
+
readonly timezoneOffset?: number | null;
|
|
12
12
|
|
|
13
13
|
constructor(input?: ParsingReference | Date) {
|
|
14
14
|
input = input ?? new Date();
|
|
15
15
|
if (input instanceof Date) {
|
|
16
16
|
this.instant = input;
|
|
17
|
-
this.timezoneOffset = -input.getTimezoneOffset();
|
|
18
17
|
} else {
|
|
19
18
|
this.instant = input.instant ?? new Date();
|
|
20
19
|
this.timezoneOffset = toTimezoneOffset(input.timezone);
|
|
@@ -133,7 +132,7 @@ export class ParsingComponents implements ParsedComponents {
|
|
|
133
132
|
toString() {
|
|
134
133
|
return `[ParsingComponents {knownValues: ${JSON.stringify(this.knownValues)}, impliedValues: ${JSON.stringify(
|
|
135
134
|
this.impliedValues
|
|
136
|
-
)}}]`;
|
|
135
|
+
)}}, reference: ${JSON.stringify(this.reference)}]`;
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
dayjs() {
|
package/src/timezone.ts
CHANGED
|
@@ -114,7 +114,7 @@ export const TIMEZONE_ABBR_MAP = {
|
|
|
114
114
|
MHT: 720,
|
|
115
115
|
MMT: 390,
|
|
116
116
|
MSD: 240,
|
|
117
|
-
MSK:
|
|
117
|
+
MSK: 180,
|
|
118
118
|
MST: -420,
|
|
119
119
|
MUT: 240,
|
|
120
120
|
MVT: 300,
|
|
@@ -192,8 +192,8 @@ export const TIMEZONE_ABBR_MAP = {
|
|
|
192
192
|
YEKT: 360,
|
|
193
193
|
};
|
|
194
194
|
|
|
195
|
-
export function toTimezoneOffset(timezoneInput
|
|
196
|
-
if (timezoneInput === null) {
|
|
195
|
+
export function toTimezoneOffset(timezoneInput?: string | number): number | null {
|
|
196
|
+
if (timezoneInput === null || timezoneInput === undefined) {
|
|
197
197
|
return null;
|
|
198
198
|
}
|
|
199
199
|
|
|
@@ -201,5 +201,5 @@ export function toTimezoneOffset(timezoneInput: string | number): number | null
|
|
|
201
201
|
return timezoneInput;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
return TIMEZONE_ABBR_MAP[timezoneInput] ??
|
|
204
|
+
return TIMEZONE_ABBR_MAP[timezoneInput] ?? null;
|
|
205
205
|
}
|
|
@@ -204,6 +204,18 @@ test("Test - Range expression", function () {
|
|
|
204
204
|
|
|
205
205
|
expect(result.end).toBeDate(new Date(2013, 10 - 1, 12, 12));
|
|
206
206
|
});
|
|
207
|
+
|
|
208
|
+
testSingleCase(chrono.de, "10. jänner 2012", new Date(2012, 7, 10), (result) => {
|
|
209
|
+
expect(result.start).not.toBeNull();
|
|
210
|
+
expect(result.start.get("year")).toBe(2012);
|
|
211
|
+
expect(result.start.get("month")).toBe(1);
|
|
212
|
+
expect(result.start.get("day")).toBe(10);
|
|
213
|
+
|
|
214
|
+
expect(result.index).toBe(0);
|
|
215
|
+
expect(result.text).toBe("10. jänner 2012");
|
|
216
|
+
|
|
217
|
+
expect(result.start).toBeDate(new Date(2012, 1 - 1, 10, 12));
|
|
218
|
+
});
|
|
207
219
|
});
|
|
208
220
|
|
|
209
221
|
test("Test - Combined expression", function () {
|