@knime/utils 1.1.3 → 1.2.0
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/intervalUtils.ts +437 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knime/utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Internal utility functions and constants shared across @knime packages.",
|
|
5
5
|
"homepage": "https://knime.github.io/webapps-common/",
|
|
6
6
|
"license": "GPL 3 and Additional Permissions according to Sec. 7 (SEE the file LICENSE)",
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
KnownHotkey as _KnownHotkey,
|
|
20
20
|
} from "./formatHotkeys";
|
|
21
21
|
import getWrappedAroundIndex from "./getWrappedAroundIndex";
|
|
22
|
+
import * as intervalUtils from "./intervalUtils";
|
|
22
23
|
import getLocalTimeZone from "./localTimezone";
|
|
23
24
|
import * as navigatorUtils from "./navigator";
|
|
24
25
|
import numIntegerDigits from "./numIntegerDigits";
|
|
@@ -65,6 +66,7 @@ export {
|
|
|
65
66
|
isAfterMaxDate,
|
|
66
67
|
isBeforeMinDate,
|
|
67
68
|
isIconExisting,
|
|
69
|
+
intervalUtils,
|
|
68
70
|
navigatorUtils,
|
|
69
71
|
numIntegerDigits,
|
|
70
72
|
sleep,
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
export type Period = {
|
|
2
|
+
negative: boolean;
|
|
3
|
+
years: number;
|
|
4
|
+
months: number;
|
|
5
|
+
weeks: number;
|
|
6
|
+
days: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type Duration = {
|
|
10
|
+
negative: boolean;
|
|
11
|
+
hours: number;
|
|
12
|
+
minutes: number;
|
|
13
|
+
seconds: number;
|
|
14
|
+
milliseconds: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type AllowedIntervalTypes = "DATE" | "TIME";
|
|
18
|
+
type AllowedIntervalTypesOrEither = AllowedIntervalTypes | "DATE_OR_TIME";
|
|
19
|
+
|
|
20
|
+
type IntervalStringValidationResult =
|
|
21
|
+
| {
|
|
22
|
+
valid: true;
|
|
23
|
+
type: AllowedIntervalTypes;
|
|
24
|
+
negative: boolean;
|
|
25
|
+
zero: boolean;
|
|
26
|
+
}
|
|
27
|
+
| { valid: false };
|
|
28
|
+
|
|
29
|
+
export type PeriodOrDuration =
|
|
30
|
+
| {
|
|
31
|
+
interval: Period;
|
|
32
|
+
type: "DATE";
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
interval: Duration;
|
|
36
|
+
type: "TIME";
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const extractPeriodFromRegexMatch = (
|
|
40
|
+
negative: boolean,
|
|
41
|
+
match: RegExpExecArray,
|
|
42
|
+
): Period => {
|
|
43
|
+
return {
|
|
44
|
+
negative,
|
|
45
|
+
years: parseInt(match[1] ?? "0", 10),
|
|
46
|
+
months: parseInt(match[2] ?? "0", 10),
|
|
47
|
+
weeks: parseInt(match[3] ?? "0", 10),
|
|
48
|
+
days: parseInt(match[4] ?? "0", 10),
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const extractDurationFromRegexMatch = (
|
|
53
|
+
negative: boolean,
|
|
54
|
+
match: RegExpExecArray,
|
|
55
|
+
): Duration => {
|
|
56
|
+
// eslint-disable-next-line no-magic-numbers
|
|
57
|
+
const milliseconds = parseInt(match[4]?.padEnd(3, "0") ?? "0", 10);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
negative,
|
|
61
|
+
hours: parseInt(match[1] ?? "0", 10),
|
|
62
|
+
minutes: parseInt(match[2] ?? "0", 10),
|
|
63
|
+
seconds: parseInt(match[3] ?? "0", 10),
|
|
64
|
+
milliseconds,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const isZero = (interval: PeriodOrDuration): boolean => {
|
|
69
|
+
if (interval.type === "DATE") {
|
|
70
|
+
return (
|
|
71
|
+
interval.interval.years === 0 &&
|
|
72
|
+
interval.interval.months === 0 &&
|
|
73
|
+
interval.interval.weeks === 0 &&
|
|
74
|
+
interval.interval.days === 0
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
return (
|
|
78
|
+
interval.interval.hours === 0 &&
|
|
79
|
+
interval.interval.minutes === 0 &&
|
|
80
|
+
interval.interval.seconds === 0 &&
|
|
81
|
+
interval.interval.milliseconds === 0
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Take an interval object and convert it to an ISO duration or period string. All parts of the
|
|
88
|
+
* relevant part of the Interval object are included in the output, even if they are zero.
|
|
89
|
+
*
|
|
90
|
+
* Technically the ISO standard allows to mix period and duration parts, but we don't support that
|
|
91
|
+
* here because it's incredibly hard to deal with all the edge cases. Maybe in a future ticket.
|
|
92
|
+
*
|
|
93
|
+
* The output here is allowed to be negative, in which case the minus sign is prepended to the
|
|
94
|
+
* output.
|
|
95
|
+
*
|
|
96
|
+
* @param toFormat The PeriodOrDuration object to format
|
|
97
|
+
*/
|
|
98
|
+
export const formatIntervalToISOIntervalString = (
|
|
99
|
+
toFormat: PeriodOrDuration,
|
|
100
|
+
): string => {
|
|
101
|
+
let output = "P";
|
|
102
|
+
|
|
103
|
+
if (toFormat.type === "DATE") {
|
|
104
|
+
output += `${toFormat.interval.years}Y${toFormat.interval.months}M${toFormat.interval.weeks}W${toFormat.interval.days}D`;
|
|
105
|
+
} else {
|
|
106
|
+
output += `T${toFormat.interval.hours}H${toFormat.interval.minutes}M${toFormat.interval.seconds}.${toFormat.interval.milliseconds
|
|
107
|
+
.toString()
|
|
108
|
+
// eslint-disable-next-line no-magic-numbers
|
|
109
|
+
.padStart(3, "0")}S`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (toFormat.interval.negative) {
|
|
113
|
+
output = `-${output}`;
|
|
114
|
+
}
|
|
115
|
+
return output;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Take an interval object and convert it to a human readable string. Only non-zero parts are
|
|
120
|
+
* included in the output (unless all parts are 0).
|
|
121
|
+
*
|
|
122
|
+
* The output here is allowed to be negative, in which case the minus sign is prepended to the
|
|
123
|
+
* output and we wrap the whole thing in brackets e.g. -(1 year 2 days).
|
|
124
|
+
*
|
|
125
|
+
* @param toFormat The PeriodOrDuration object to format
|
|
126
|
+
*/
|
|
127
|
+
export const formatIntervalToHumanReadableIntervalString = (
|
|
128
|
+
toFormat: PeriodOrDuration,
|
|
129
|
+
): string => {
|
|
130
|
+
const formatPart = (value: number, unit: string): string => {
|
|
131
|
+
if (value === 0) {
|
|
132
|
+
return "";
|
|
133
|
+
} else if (value === 1) {
|
|
134
|
+
return `${value} ${unit} `;
|
|
135
|
+
} else {
|
|
136
|
+
return `${value} ${unit}s `;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
let output = "";
|
|
141
|
+
|
|
142
|
+
if (toFormat.type === "DATE") {
|
|
143
|
+
if (isZero(toFormat)) {
|
|
144
|
+
return "0 days";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
output += formatPart(toFormat.interval.years, "year");
|
|
148
|
+
output += formatPart(toFormat.interval.months, "month");
|
|
149
|
+
output += formatPart(toFormat.interval.weeks, "week");
|
|
150
|
+
output += formatPart(toFormat.interval.days, "day");
|
|
151
|
+
} else {
|
|
152
|
+
if (isZero(toFormat)) {
|
|
153
|
+
return "0 seconds";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
output += formatPart(toFormat.interval.hours, "hour");
|
|
157
|
+
output += formatPart(toFormat.interval.minutes, "minute");
|
|
158
|
+
|
|
159
|
+
const milliSecondsZero = toFormat.interval.milliseconds === 0;
|
|
160
|
+
if (toFormat.interval.seconds !== 0 || !milliSecondsZero) {
|
|
161
|
+
if (milliSecondsZero) {
|
|
162
|
+
output += formatPart(toFormat.interval.seconds, "second");
|
|
163
|
+
} else {
|
|
164
|
+
output += `${toFormat.interval.seconds}.${toFormat.interval.milliseconds.toString().padStart(3, "0")} seconds `;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (toFormat.interval.negative) {
|
|
170
|
+
output = `-(${output.trim()})`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return output.trim();
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const yearsPart = "(\\d+)\\s*y(?:ears?)?";
|
|
177
|
+
const monthsPart = "(\\d+)\\s*m(?:o(?:nths?)?)?";
|
|
178
|
+
const weeksPart = "(\\d+)\\s*w(?:eeks?)?";
|
|
179
|
+
const daysPart = "(\\d+)\\s*d(?:ays?)?";
|
|
180
|
+
const hoursPart = "(\\d+)\\s*h(?:ours?)?";
|
|
181
|
+
const minutesPart = "(\\d+)\\s*m(?:in(?:s|utes?)?)?";
|
|
182
|
+
const secondsPart = "(\\d+)(?:[,.](\\d{1,3}))?\\s*s(?:ec(?:s|onds?)?)?";
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Matches a human readable period string, e.g. "1y 2mo 3weeks 4day" (each part is optional)
|
|
186
|
+
*/
|
|
187
|
+
const humanReadablePeriodRegex = new RegExp(
|
|
188
|
+
`^(?:${yearsPart})?\\s*(?:${monthsPart})?\\s*(?:${weeksPart})?\\s*(?:${daysPart})?\\s*$`,
|
|
189
|
+
"i",
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Matches a human readable duration string, e.g. "1h 2m 3.456s" (each part is optional)
|
|
194
|
+
*/
|
|
195
|
+
const humanReadableDurationRegex = new RegExp(
|
|
196
|
+
`^(?:${hoursPart})?\\s*(?:${minutesPart})?\\s*(?:${secondsPart})?$`,
|
|
197
|
+
"i",
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Matches a negative human readable period string, e.g. "-(1y 2mo 3weeks 4day)" (each part is optional)
|
|
202
|
+
*/
|
|
203
|
+
const negativeHumanReadablePeriodRegex = new RegExp(
|
|
204
|
+
`^\\s*-\\s*\\(\\s*${humanReadablePeriodRegex.source.replace(/[$^]/g, "")}\\s*\\)\\s*$`,
|
|
205
|
+
"i",
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Matches a negative human readable period string with one field like "-1y" or "-1w"
|
|
210
|
+
*/
|
|
211
|
+
const negativeHumanReadablePeriodRegexWithSingleField = new RegExp(
|
|
212
|
+
`^\\s*-\\s*(?:\\s*${yearsPart}|${monthsPart}|${weeksPart}|${daysPart})\\s*$`,
|
|
213
|
+
"i",
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Matches a negative human readable duration string, e.g. "-(1h 2m 3.456s)" (each part is optional)
|
|
218
|
+
*/
|
|
219
|
+
const negativeHumanReadableDurationRegex = new RegExp(
|
|
220
|
+
`^\\s*-\\s*\\(\\s*${humanReadableDurationRegex.source.replace(/[$^]/g, "")}\\s*\\)\\s*$`,
|
|
221
|
+
"i",
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Matches a negative human readable duration string with one field like "-1h" or "-1.5s"
|
|
226
|
+
*/
|
|
227
|
+
const negativeHumanReadableDurationRegexWithSingleField = new RegExp(
|
|
228
|
+
`^\\s*-\\s*(?:\\s*${hoursPart}|${minutesPart}|${secondsPart})\\s*$`,
|
|
229
|
+
"i",
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Try to parse a human readable interval string into an Interval object. Any parts not specified
|
|
234
|
+
* are assumed to be zero. Note that the string must be either a period or a duration, not a mix of
|
|
235
|
+
* both. So for example, "1y 2mo 3weeks 4day" is fine, as is "1h 2m 3.456s". However, "1y 2mo 1sec"
|
|
236
|
+
* is not, since it's a mix of both.
|
|
237
|
+
*
|
|
238
|
+
* Negative intervals are allowed, although if multiple fields are provided the string must include
|
|
239
|
+
* brackets, so "-(1y 2mo)" is also valid, as is "-5 seconds".
|
|
240
|
+
*
|
|
241
|
+
* The string is required to have at least some content, any string that is blank will throw an error.
|
|
242
|
+
*
|
|
243
|
+
* @param intervalString The human readable time string
|
|
244
|
+
*/
|
|
245
|
+
export const parseHumanReadableIntervalString = (
|
|
246
|
+
intervalString: string,
|
|
247
|
+
): PeriodOrDuration => {
|
|
248
|
+
intervalString = intervalString?.trim();
|
|
249
|
+
|
|
250
|
+
if (!intervalString) {
|
|
251
|
+
throw new Error("Empty interval string");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const matchPositivePeriod = humanReadablePeriodRegex.exec(intervalString);
|
|
255
|
+
const matchNegativePeriod =
|
|
256
|
+
negativeHumanReadablePeriodRegex.exec(intervalString) ??
|
|
257
|
+
negativeHumanReadablePeriodRegexWithSingleField.exec(intervalString);
|
|
258
|
+
const matchPositiveDuration = humanReadableDurationRegex.exec(intervalString);
|
|
259
|
+
const matchNegativeDuration =
|
|
260
|
+
negativeHumanReadableDurationRegex.exec(intervalString) ??
|
|
261
|
+
negativeHumanReadableDurationRegexWithSingleField.exec(intervalString);
|
|
262
|
+
|
|
263
|
+
const matchPeriod = matchPositivePeriod ?? matchNegativePeriod;
|
|
264
|
+
const matchDuration = matchPositiveDuration ?? matchNegativeDuration;
|
|
265
|
+
const negative = (matchNegativePeriod ?? matchNegativeDuration) !== null;
|
|
266
|
+
|
|
267
|
+
// I think this will happen only for the string "1M" or similar
|
|
268
|
+
if (matchDuration && matchPeriod) {
|
|
269
|
+
throw new Error("Ambiguous time format");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (matchPeriod) {
|
|
273
|
+
return {
|
|
274
|
+
type: "DATE",
|
|
275
|
+
interval: extractPeriodFromRegexMatch(negative, matchPeriod),
|
|
276
|
+
};
|
|
277
|
+
} else if (matchDuration) {
|
|
278
|
+
return {
|
|
279
|
+
type: "TIME",
|
|
280
|
+
interval: extractDurationFromRegexMatch(negative, matchDuration),
|
|
281
|
+
};
|
|
282
|
+
} else {
|
|
283
|
+
throw new Error(`Invalid human readable time: ${intervalString}`);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Check if the given string can be understood as a human readable duration or period string. The
|
|
289
|
+
* type parameter specifies whether the string should be a date, time, or either.
|
|
290
|
+
*
|
|
291
|
+
* @param time the string to check
|
|
292
|
+
* @param expectedType the type of the string - either "DATE", "TIME", or "ANY"
|
|
293
|
+
*/
|
|
294
|
+
export const isValidHumanReadableIntervalString = (
|
|
295
|
+
time: string,
|
|
296
|
+
expectedType: AllowedIntervalTypesOrEither,
|
|
297
|
+
): IntervalStringValidationResult => {
|
|
298
|
+
try {
|
|
299
|
+
const validationResult = parseHumanReadableIntervalString(time);
|
|
300
|
+
const actualType = validationResult.type;
|
|
301
|
+
const negative = validationResult.interval.negative;
|
|
302
|
+
|
|
303
|
+
if (actualType === expectedType || expectedType === "DATE_OR_TIME") {
|
|
304
|
+
return {
|
|
305
|
+
valid: true,
|
|
306
|
+
type: actualType,
|
|
307
|
+
negative,
|
|
308
|
+
zero: isZero(validationResult),
|
|
309
|
+
};
|
|
310
|
+
} else {
|
|
311
|
+
return { valid: false };
|
|
312
|
+
}
|
|
313
|
+
} catch (e) {
|
|
314
|
+
return { valid: false };
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Convert an ISO duration OR period string to an Interval object. So for example,
|
|
320
|
+
* "P1Y2M3W4D" is fine, as is "PT1H2M3.456S". However, "P1Y2M3W4D1H2M3.456S" is not, since
|
|
321
|
+
* it's a mix of both. We're more forgiving than the ISO standard, so we allow for missing parts.
|
|
322
|
+
* Also, the string is allowed to be negative, so "-P1Y2M3W4D" is also valid.
|
|
323
|
+
*
|
|
324
|
+
* The string is required to have at least some content, so the strings "", "P", and "PT" are
|
|
325
|
+
* considered invalid (also if they start with a -).
|
|
326
|
+
*
|
|
327
|
+
* @param isoString The ISO duration or period string.
|
|
328
|
+
*/
|
|
329
|
+
export const parseISOIntervalString = (isoString: string): PeriodOrDuration => {
|
|
330
|
+
isoString = isoString?.trim();
|
|
331
|
+
|
|
332
|
+
if (["", "P", "PT", "-", "-P", "-PT"].includes(isoString)) {
|
|
333
|
+
throw new Error("Empty ISO duration");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const periodRegex = /^-?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?$/;
|
|
337
|
+
const durationRegex =
|
|
338
|
+
/^-?PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:[,.](\d{1,3}))?S)?$/;
|
|
339
|
+
|
|
340
|
+
const matchPeriod = periodRegex.exec(isoString);
|
|
341
|
+
const matchDuration = durationRegex.exec(isoString);
|
|
342
|
+
|
|
343
|
+
if (matchPeriod) {
|
|
344
|
+
return {
|
|
345
|
+
type: "DATE",
|
|
346
|
+
interval: extractPeriodFromRegexMatch(
|
|
347
|
+
isoString.startsWith("-"),
|
|
348
|
+
matchPeriod,
|
|
349
|
+
),
|
|
350
|
+
};
|
|
351
|
+
} else if (matchDuration) {
|
|
352
|
+
return {
|
|
353
|
+
type: "TIME",
|
|
354
|
+
interval: extractDurationFromRegexMatch(
|
|
355
|
+
isoString.startsWith("-"),
|
|
356
|
+
matchDuration,
|
|
357
|
+
),
|
|
358
|
+
};
|
|
359
|
+
} else {
|
|
360
|
+
throw new Error(`Invalid ISO duration: ${isoString}`);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Check if the given string is a valid duration or period ISO string. The type parameter
|
|
366
|
+
* specifies whether the string should be a date, time, or either.
|
|
367
|
+
*
|
|
368
|
+
* @param isoString the string to check
|
|
369
|
+
* @param type the type of the string - either "DATE", "TIME", or "ANY"
|
|
370
|
+
*/
|
|
371
|
+
export const isValidISOIntervalString = (
|
|
372
|
+
isoString: string,
|
|
373
|
+
type: AllowedIntervalTypesOrEither,
|
|
374
|
+
): IntervalStringValidationResult => {
|
|
375
|
+
try {
|
|
376
|
+
const validationResult = parseISOIntervalString(isoString);
|
|
377
|
+
const actualType = validationResult.type;
|
|
378
|
+
const negative = validationResult.interval.negative;
|
|
379
|
+
|
|
380
|
+
if (actualType === type || type === "DATE_OR_TIME") {
|
|
381
|
+
return {
|
|
382
|
+
valid: true,
|
|
383
|
+
type: actualType,
|
|
384
|
+
negative,
|
|
385
|
+
zero: isZero(validationResult),
|
|
386
|
+
};
|
|
387
|
+
} else {
|
|
388
|
+
return { valid: false };
|
|
389
|
+
}
|
|
390
|
+
} catch (e) {
|
|
391
|
+
return { valid: false };
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Try to convert an interval string to an Interval object. The string can be either an ISO
|
|
397
|
+
* duration/period string or a human readable string. If the string is not valid, an error is
|
|
398
|
+
* thrown.
|
|
399
|
+
*
|
|
400
|
+
* @param time the time string, either ISO or human readable
|
|
401
|
+
*/
|
|
402
|
+
export const parseGenericIntervalString = (time: string): PeriodOrDuration => {
|
|
403
|
+
const validISO = isValidISOIntervalString(time, "DATE_OR_TIME");
|
|
404
|
+
const validHumanReadable = isValidHumanReadableIntervalString(
|
|
405
|
+
time,
|
|
406
|
+
"DATE_OR_TIME",
|
|
407
|
+
);
|
|
408
|
+
if (validISO.valid) {
|
|
409
|
+
return parseISOIntervalString(time);
|
|
410
|
+
} else if (validHumanReadable.valid) {
|
|
411
|
+
return parseHumanReadableIntervalString(time);
|
|
412
|
+
} else {
|
|
413
|
+
throw new Error(`Invalid interval string: ${time}`);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Check if the given string is a valid interval string, either human readable or ISO. The type parameter
|
|
419
|
+
* specifies whether the string should be a period, duration, or either.
|
|
420
|
+
*
|
|
421
|
+
* @param time the time string to check
|
|
422
|
+
* @param type the type of the string - either "DATE", "TIME", or "ANY"
|
|
423
|
+
*/
|
|
424
|
+
export const isValidIntervalString = (
|
|
425
|
+
time: string,
|
|
426
|
+
type: AllowedIntervalTypesOrEither,
|
|
427
|
+
): IntervalStringValidationResult => {
|
|
428
|
+
const validISO = isValidISOIntervalString(time, type);
|
|
429
|
+
const validHumanReadable = isValidHumanReadableIntervalString(time, type);
|
|
430
|
+
if (validISO.valid) {
|
|
431
|
+
return validISO;
|
|
432
|
+
} else if (validHumanReadable.valid) {
|
|
433
|
+
return validHumanReadable;
|
|
434
|
+
} else {
|
|
435
|
+
return { valid: false };
|
|
436
|
+
}
|
|
437
|
+
};
|