@jarenjs/core 0.9.2 → 0.34.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/ARCHITECTURE.md +229 -26
- package/README.md +64 -10
- package/dist/types/array.d.ts +13 -0
- package/dist/types/cache.d.ts +166 -0
- package/dist/types/chunk.d.ts +102 -0
- package/dist/types/color.d.ts +64 -0
- package/dist/types/convert/convert.d.ts +33 -0
- package/dist/types/convert/currency.d.ts +47 -0
- package/dist/types/convert/index.d.ts +9 -0
- package/dist/types/convert/registry.d.ts +55 -0
- package/dist/types/dates/civil.d.ts +146 -0
- package/dist/types/dates/duration.d.ts +83 -0
- package/dist/types/dates/format.d.ts +59 -0
- package/dist/types/dates/index.d.ts +4 -0
- package/dist/types/{dates.d.ts → dates/rfc3339.d.ts} +57 -3
- package/dist/types/errors.d.ts +77 -0
- package/dist/types/finance/amortization.d.ts +51 -0
- package/dist/types/finance/bond.d.ts +45 -0
- package/dist/types/finance/cashflow.d.ts +44 -0
- package/dist/types/finance/depreciation.d.ts +26 -0
- package/dist/types/finance/index.d.ts +16 -0
- package/dist/types/finance/indicators.d.ts +76 -0
- package/dist/types/finance/interest.d.ts +44 -0
- package/dist/types/finance/returns.d.ts +43 -0
- package/dist/types/finance/tvm.d.ts +50 -0
- package/dist/types/geo/angle.d.ts +4 -0
- package/dist/types/geo/bbox.d.ts +32 -0
- package/dist/types/geo/distance.d.ts +67 -0
- package/dist/types/geo/geohash.d.ts +63 -0
- package/dist/types/geo/geojson.d.ts +108 -0
- package/dist/types/geo/index-tree.d.ts +52 -0
- package/dist/types/geo/index.d.ts +11 -0
- package/dist/types/geo/mercator.d.ts +57 -0
- package/dist/types/geo/predicates.d.ts +44 -0
- package/dist/types/geo/ring.d.ts +80 -0
- package/dist/types/geo/simplify.d.ts +31 -0
- package/dist/types/geo/valid.d.ts +24 -0
- package/dist/types/geo/wkt.d.ts +16 -0
- package/dist/types/math/float64.d.ts +126 -3
- package/dist/types/math/format.d.ts +58 -0
- package/dist/types/math/index.d.ts +5 -0
- package/dist/types/math/mat4.d.ts +60 -0
- package/dist/types/math/project.d.ts +54 -0
- package/dist/types/math/solve.d.ts +67 -0
- package/dist/types/math/word.d.ts +82 -0
- package/dist/types/message.d.ts +52 -0
- package/dist/types/object.d.ts +151 -0
- package/dist/types/scan.d.ts +30 -0
- package/dist/types/schema.d.ts +24 -0
- package/dist/types/string.d.ts +96 -0
- package/dist/types/text/base64.d.ts +6 -4
- package/dist/types/text/email.d.ts +0 -1
- package/dist/types/text/host.d.ts +39 -8
- package/dist/types/text/i18n.d.ts +13 -1
- package/dist/types/text/index.d.ts +1 -0
- package/dist/types/text/misc.d.ts +15 -1
- package/dist/types/text/punycode.d.ts +45 -85
- package/docs/CONVERT.md +45 -0
- package/docs/DATES.md +78 -0
- package/docs/FINANCE.md +59 -0
- package/docs/GEO.md +114 -0
- package/docs/MATH.md +75 -0
- package/package.json +57 -4
- package/src/array.js +16 -0
- package/src/cache.js +206 -0
- package/src/chunk.js +159 -0
- package/src/color.js +125 -0
- package/src/convert/convert.js +59 -0
- package/src/convert/currency.js +74 -0
- package/src/convert/index.js +11 -0
- package/src/convert/registry.js +213 -0
- package/src/dates/civil.js +360 -0
- package/src/dates/duration.js +225 -0
- package/src/dates/format.js +238 -0
- package/src/dates/index.js +30 -0
- package/src/dates/rfc3339.js +621 -0
- package/src/errors.js +102 -0
- package/src/finance/amortization.js +50 -0
- package/src/finance/bond.js +100 -0
- package/src/finance/cashflow.js +122 -0
- package/src/finance/depreciation.js +54 -0
- package/src/finance/index.js +27 -0
- package/src/finance/indicators.js +207 -0
- package/src/finance/interest.js +63 -0
- package/src/finance/returns.js +90 -0
- package/src/finance/tvm.js +94 -0
- package/src/function.js +0 -2
- package/src/geo/angle.js +12 -0
- package/src/geo/bbox.js +80 -0
- package/src/geo/distance.js +155 -0
- package/src/geo/geohash.js +224 -0
- package/src/geo/geojson.js +361 -0
- package/src/geo/index-tree.js +294 -0
- package/src/geo/index.js +52 -0
- package/src/geo/mercator.js +124 -0
- package/src/geo/predicates.js +313 -0
- package/src/geo/ring.js +182 -0
- package/src/geo/simplify.js +130 -0
- package/src/geo/valid.js +142 -0
- package/src/geo/wkt.js +262 -0
- package/src/math/float64.js +228 -11
- package/src/math/format.js +157 -0
- package/src/math/index.js +5 -0
- package/src/math/mat4.js +131 -0
- package/src/math/project.js +49 -0
- package/src/math/solve.js +112 -0
- package/src/math/word.js +177 -0
- package/src/message.js +119 -0
- package/src/object.js +329 -0
- package/src/scan.js +42 -0
- package/src/schema.js +37 -0
- package/src/string.js +189 -0
- package/src/text/base64.js +15 -52
- package/src/text/email.js +0 -5
- package/src/text/host.js +515 -140
- package/src/text/i18n.js +13 -6
- package/src/text/identifiers.js +1 -1
- package/src/text/index.js +1 -0
- package/src/text/misc.js +81 -5
- package/src/text/punycode.js +235 -323
- package/src/dates.js +0 -371
package/src/dates.js
DELETED
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
isStringType,
|
|
5
|
-
isObjectOfClass,
|
|
6
|
-
} from './index.js';
|
|
7
|
-
|
|
8
|
-
//#region Dates Constants
|
|
9
|
-
export const CONST_TICKS_SECOND = 1000;
|
|
10
|
-
export const CONST_TICKS_HOUR = CONST_TICKS_SECOND * 60 * 60;
|
|
11
|
-
export const CONST_TICKS_DAY = CONST_TICKS_HOUR * 24;
|
|
12
|
-
|
|
13
|
-
export const CONST_TIME_INSERTDATE = '1970-01-01T';
|
|
14
|
-
export const CONST_DATE_APPENDTIME = 'T00:00:00Z';
|
|
15
|
-
|
|
16
|
-
export const CONST_RFC3339_DAYS = Object.freeze(
|
|
17
|
-
[0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6
|
|
21
|
-
export const CONST_RFC3339_REGEX_ISDATE = /^(\d\d\d\d)-([0-1]\d)-([0-3]\d)z?$/i;
|
|
22
|
-
|
|
23
|
-
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6
|
|
24
|
-
export const CONST_RFC3339_REGEX_ISTIME = /^(\d\d):(\d\d):(\d\d)(\.\d{1,6})?(z|(([+-])(\d\d):(\d\d)))$/i;
|
|
25
|
-
|
|
26
|
-
//#endregion
|
|
27
|
-
|
|
28
|
-
//#region Dates Compare
|
|
29
|
-
export function isDateType(data) {
|
|
30
|
-
return isObjectOfClass(data, Date);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function isDateishType(data) {
|
|
34
|
-
return isDateType(data)
|
|
35
|
-
|| !Number.isNaN(Date.parse(data));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function isLeapYear(year) {
|
|
39
|
-
// https://tools.ietf.org/html/rfc3339#appendix-C
|
|
40
|
-
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function isDateOnlyInRange(year = 0, month = 0, day = 0) {
|
|
44
|
-
return month >= 1
|
|
45
|
-
&& month <= 12
|
|
46
|
-
&& day >= 1
|
|
47
|
-
&& day <= (month === 2 && isLeapYear(year)
|
|
48
|
-
? 29
|
|
49
|
-
: CONST_RFC3339_DAYS[month]);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function isDateOnlyRFC3339(str) {
|
|
53
|
-
if (!isStringType(str))
|
|
54
|
-
return false;
|
|
55
|
-
|
|
56
|
-
const r = str.match(CONST_RFC3339_REGEX_ISDATE);
|
|
57
|
-
if (r == null)
|
|
58
|
-
return false;
|
|
59
|
-
|
|
60
|
-
const y = parseInt(r[1], 10) | 0;
|
|
61
|
-
const m = parseInt(r[2], 10) | 0;
|
|
62
|
-
const d = parseInt(r[3], 10) | 0;
|
|
63
|
-
return isDateOnlyInRange(y, m, d);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function isTimeOnlyInRange(hrs = 0, min = 0, sec = 0, tzh = 0, tzm = 0, tzSign = 1) {
|
|
67
|
-
// Validate timezone offset range
|
|
68
|
-
if (tzh < 0 || tzh > 23 || tzm < 0 || tzm > 59)
|
|
69
|
-
return false;
|
|
70
|
-
|
|
71
|
-
// For leap seconds (sec === 60), we need to check if the corresponding UTC time
|
|
72
|
-
// is 23:59:60. A leap second is only valid at the very end of a UTC day.
|
|
73
|
-
if (sec === 60) {
|
|
74
|
-
// Calculate what the UTC time would be
|
|
75
|
-
// UTC = local - offset (if offset is positive, local is ahead of UTC)
|
|
76
|
-
// So: UTC = local + (tzSign * tzh) hours + (tzSign * tzm) minutes
|
|
77
|
-
let utcHrs = hrs - (tzSign * tzh);
|
|
78
|
-
let utcMin = min - (tzSign * tzm);
|
|
79
|
-
|
|
80
|
-
// Handle wrap-around
|
|
81
|
-
while (utcMin < 0) {
|
|
82
|
-
utcMin += 60;
|
|
83
|
-
utcHrs -= 1;
|
|
84
|
-
}
|
|
85
|
-
while (utcMin >= 60) {
|
|
86
|
-
utcMin -= 60;
|
|
87
|
-
utcHrs += 1;
|
|
88
|
-
}
|
|
89
|
-
while (utcHrs < 0) {
|
|
90
|
-
utcHrs += 24;
|
|
91
|
-
}
|
|
92
|
-
utcHrs = utcHrs % 24;
|
|
93
|
-
|
|
94
|
-
// Leap second is only valid at 23:59:60 UTC
|
|
95
|
-
return utcHrs === 23 && utcMin === 59 && sec === 60;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Normal time validation (sec 0-59)
|
|
99
|
-
return hrs >= 0 && hrs <= 23
|
|
100
|
-
&& min >= 0 && min <= 59
|
|
101
|
-
&& sec >= 0 && sec <= 59;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function isTimeOnlyRFC3339(str) {
|
|
105
|
-
if (!isStringType(str))
|
|
106
|
-
return false;
|
|
107
|
-
|
|
108
|
-
const r = str.match(CONST_RFC3339_REGEX_ISTIME);
|
|
109
|
-
if (r == null)
|
|
110
|
-
return false;
|
|
111
|
-
|
|
112
|
-
const h = parseInt(r[1], 10) | 0;
|
|
113
|
-
const m = parseInt(r[2], 10) | 0;
|
|
114
|
-
const s = parseInt(r[3], 10) | 0;
|
|
115
|
-
const th = parseInt(r[8], 10) | 0;
|
|
116
|
-
const tm = parseInt(r[9], 10) | 0;
|
|
117
|
-
// r[7] is the timezone sign (+ or -)
|
|
118
|
-
// + means local time is ahead of UTC, so we subtract to get UTC
|
|
119
|
-
// - means local time is behind UTC, so we add to get UTC
|
|
120
|
-
const tzSign = r[7] === '-' ? -1 : 1;
|
|
121
|
-
return isTimeOnlyInRange(h, m, s, th, tm, tzSign);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function isDateTimeRFC3339(str) {
|
|
125
|
-
// http://tools.ietf.org/html/rfc3339#section-5.6
|
|
126
|
-
if (!isStringType(str)) return false;
|
|
127
|
-
const dateTime = str.split(/t|\s/i);
|
|
128
|
-
return dateTime.length === 2
|
|
129
|
-
&& isDateOnlyRFC3339(dateTime[0])
|
|
130
|
-
&& isTimeOnlyRFC3339(dateTime[1]);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
//#endregion
|
|
134
|
-
|
|
135
|
-
//#region Dates Getters
|
|
136
|
-
export function getDateTypeOfDateOnlyRFC3339(str, def = undefined) {
|
|
137
|
-
return isDateOnlyRFC3339(str)
|
|
138
|
-
? new Date(Date.parse(str))
|
|
139
|
-
: def;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function getDateTypeOfTimeOnlyRFC3339(str, def = undefined) {
|
|
143
|
-
return isTimeOnlyRFC3339(str)
|
|
144
|
-
? new Date(Date.parse(CONST_TIME_INSERTDATE + str))
|
|
145
|
-
: def;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function getDateTypeOfDateTimeRFC3339(str, def = undefined) {
|
|
149
|
-
return isDateTimeRFC3339(str)
|
|
150
|
-
? new Date(Date.parse(str))
|
|
151
|
-
: def;
|
|
152
|
-
}
|
|
153
|
-
//#endregion
|
|
154
|
-
|
|
155
|
-
//#region Duration Validation (RFC 3339)
|
|
156
|
-
// Duration format: P[n]Y[n]M[n]DT[n]H[n]M[n]S or P[n]W
|
|
157
|
-
// Examples: P1Y2M3DT4H5M6S, P1W, PT1H, P1Y
|
|
158
|
-
// https://tools.ietf.org/html/rfc3339#appendix-A
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Validates a duration string per RFC 3339.
|
|
162
|
-
* Duration format: P[n]Y[n]M[n]DT[n]H[n]M[n]S or P[n]W
|
|
163
|
-
*
|
|
164
|
-
* @param {string} str - The duration string to validate
|
|
165
|
-
* @returns {boolean} - True if the string is a valid duration
|
|
166
|
-
* @example
|
|
167
|
-
* isValidDuration('P1Y2M3DT4H5M6S'); // true (1 year, 2 months, 3 days, 4 hours, 5 minutes, 6 seconds)
|
|
168
|
-
* isValidDuration('P1W'); // true (1 week)
|
|
169
|
-
* isValidDuration('PT1H'); // true (1 hour)
|
|
170
|
-
* isValidDuration('P1Y'); // true (1 year)
|
|
171
|
-
* isValidDuration('P'); // false (empty duration)
|
|
172
|
-
* isValidDuration('1Y'); // false (missing P)
|
|
173
|
-
*/
|
|
174
|
-
export function isValidDuration(str) {
|
|
175
|
-
if (!isStringType(str))
|
|
176
|
-
return false;
|
|
177
|
-
|
|
178
|
-
// Must start with P
|
|
179
|
-
if (!str.startsWith('P'))
|
|
180
|
-
return false;
|
|
181
|
-
|
|
182
|
-
// Cannot be just "P"
|
|
183
|
-
if (str.length < 2)
|
|
184
|
-
return false;
|
|
185
|
-
|
|
186
|
-
// Parse and validate components
|
|
187
|
-
// Remove the 'P' prefix
|
|
188
|
-
const rest = str.slice(1);
|
|
189
|
-
|
|
190
|
-
// Check for week format: P[n]W (cannot be combined with other components)
|
|
191
|
-
if (rest.endsWith('W')) {
|
|
192
|
-
const weekPart = rest.slice(0, -1);
|
|
193
|
-
return weekPart.length > 0 && /^\d+$/.test(weekPart);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Split into date and time parts
|
|
197
|
-
const tIndex = rest.indexOf('T');
|
|
198
|
-
const datePart = tIndex >= 0 ? rest.slice(0, tIndex) : rest;
|
|
199
|
-
const timePart = tIndex >= 0 ? rest.slice(tIndex + 1) : '';
|
|
200
|
-
|
|
201
|
-
// Must have at least one component
|
|
202
|
-
if (!datePart && !timePart)
|
|
203
|
-
return false;
|
|
204
|
-
|
|
205
|
-
// If there's a T, there must be at least one time component
|
|
206
|
-
if (tIndex >= 0 && !timePart)
|
|
207
|
-
return false;
|
|
208
|
-
|
|
209
|
-
// Validate date part components (Y, M, D)
|
|
210
|
-
if (datePart) {
|
|
211
|
-
// Must match pattern: optional number+Y, optional number+M, optional number+D
|
|
212
|
-
// in that order, at least one must be present
|
|
213
|
-
const dateRegex = /^(\d+Y)?(\d+M)?(\d+D)?$/;
|
|
214
|
-
if (!dateRegex.test(datePart))
|
|
215
|
-
return false;
|
|
216
|
-
// Must have at least one component
|
|
217
|
-
if (!/\d+[YMD]/.test(datePart))
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Validate time part components (H, M, S)
|
|
222
|
-
if (timePart) {
|
|
223
|
-
const timeRegex = /^(\d+H)?(\d+M)?(\d+(?:\.\d+)?S)?$/;
|
|
224
|
-
if (!timeRegex.test(timePart))
|
|
225
|
-
return false;
|
|
226
|
-
// Must have at least one component
|
|
227
|
-
if (!/\d+[HMS]/.test(timePart))
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return true;
|
|
232
|
-
}
|
|
233
|
-
//#endregion
|
|
234
|
-
|
|
235
|
-
//#region ISO Date-Time and ISO Time (with optional timezone)
|
|
236
|
-
// ISO 8601 date-time with optional timezone (like 2024-01-15T12:30:00)
|
|
237
|
-
// ISO 8601 time with optional timezone (like 12:30:00)
|
|
238
|
-
|
|
239
|
-
// Regex for iso-date-time: allows with or without timezone
|
|
240
|
-
const CONST_ISO_REGEX_DATETIME = /^(\d{4})-([0-1]\d)-([0-3]\d)[T\s](\d{2}):(\d{2}):(\d{2})(\.\d{1,6})?(?:(Z)|([+-])(\d{2}):(\d{2}))?$/i;
|
|
241
|
-
|
|
242
|
-
// Regex for iso-time: allows with or without timezone
|
|
243
|
-
const CONST_ISO_REGEX_TIME = /^(\d{2}):(\d{2}):(\d{2})(\.\d{1,6})?(?:(Z)|([+-])(\d{2}):(\d{2}))?$/i;
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Validates an ISO 8601 date-time string with optional timezone.
|
|
247
|
-
* Unlike RFC 3339, the timezone is optional.
|
|
248
|
-
*
|
|
249
|
-
* @param {string} str - The date-time string to validate
|
|
250
|
-
* @returns {boolean} - True if the string is a valid ISO date-time
|
|
251
|
-
* @example
|
|
252
|
-
* isValidISODateTime('2024-01-15T12:30:00Z'); // true
|
|
253
|
-
* isValidISODateTime('2024-01-15T12:30:00+01:00'); // true
|
|
254
|
-
* isValidISODateTime('2024-01-15T12:30:00'); // true (no timezone)
|
|
255
|
-
* isValidISODateTime('2024-01-15 12:30:00'); // true (space separator)
|
|
256
|
-
* isValidISODateTime('2024-13-15T12:30:00'); // false (invalid month)
|
|
257
|
-
*/
|
|
258
|
-
export function isValidISODateTime(str) {
|
|
259
|
-
if (!isStringType(str))
|
|
260
|
-
return false;
|
|
261
|
-
|
|
262
|
-
const r = str.match(CONST_ISO_REGEX_DATETIME);
|
|
263
|
-
if (r == null)
|
|
264
|
-
return false;
|
|
265
|
-
|
|
266
|
-
const y = parseInt(r[1], 10) | 0;
|
|
267
|
-
const m = parseInt(r[2], 10) | 0;
|
|
268
|
-
const d = parseInt(r[3], 10) | 0;
|
|
269
|
-
const h = parseInt(r[4], 10) | 0;
|
|
270
|
-
const min = parseInt(r[5], 10) | 0;
|
|
271
|
-
const s = parseInt(r[6], 10) | 0;
|
|
272
|
-
|
|
273
|
-
// Validate date portion
|
|
274
|
-
if (!isDateOnlyInRange(y, m, d))
|
|
275
|
-
return false;
|
|
276
|
-
|
|
277
|
-
// Validate time portion (no leap seconds for ISO date-time without explicit timezone)
|
|
278
|
-
if (h < 0 || h > 23 || min < 0 || min > 59 || s < 0 || s > 59)
|
|
279
|
-
return false;
|
|
280
|
-
|
|
281
|
-
// Validate timezone if present
|
|
282
|
-
if (r[9] != null) {
|
|
283
|
-
const tzh = parseInt(r[10], 10) | 0;
|
|
284
|
-
const tzm = parseInt(r[11], 10) | 0;
|
|
285
|
-
if (tzh < 0 || tzh > 23 || tzm < 0 || tzm > 59)
|
|
286
|
-
return false;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return true;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Validates an ISO 8601 time string with optional timezone.
|
|
294
|
-
* Unlike RFC 3339, the timezone is optional.
|
|
295
|
-
*
|
|
296
|
-
* @param {string} str - The time string to validate
|
|
297
|
-
* @returns {boolean} - True if the string is a valid ISO time
|
|
298
|
-
* @example
|
|
299
|
-
* isValidISOTime('12:30:00Z'); // true
|
|
300
|
-
* isValidISOTime('12:30:00+01:00'); // true
|
|
301
|
-
* isValidISOTime('12:30:00'); // true (no timezone)
|
|
302
|
-
* isValidISOTime('25:00:00'); // false (invalid hour)
|
|
303
|
-
*/
|
|
304
|
-
export function isValidISOTime(str) {
|
|
305
|
-
if (!isStringType(str))
|
|
306
|
-
return false;
|
|
307
|
-
|
|
308
|
-
const r = str.match(CONST_ISO_REGEX_TIME);
|
|
309
|
-
if (r == null)
|
|
310
|
-
return false;
|
|
311
|
-
|
|
312
|
-
const h = parseInt(r[1], 10) | 0;
|
|
313
|
-
const m = parseInt(r[2], 10) | 0;
|
|
314
|
-
const s = parseInt(r[3], 10) | 0;
|
|
315
|
-
|
|
316
|
-
// Validate time portion (no leap seconds for ISO time without explicit timezone)
|
|
317
|
-
if (h < 0 || h > 23 || m < 0 || m > 59 || s < 0 || s > 59)
|
|
318
|
-
return false;
|
|
319
|
-
|
|
320
|
-
// Validate timezone if present
|
|
321
|
-
if (r[7] != null) {
|
|
322
|
-
const tzh = parseInt(r[8], 10) | 0;
|
|
323
|
-
const tzm = parseInt(r[9], 10) | 0;
|
|
324
|
-
if (tzh < 0 || tzh > 23 || tzm < 0 || tzm > 59)
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return true;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Parses an ISO 8601 date-time string with optional timezone.
|
|
333
|
-
* Returns a Date object if valid, otherwise undefined.
|
|
334
|
-
*
|
|
335
|
-
* @param {string} str - The date-time string to parse
|
|
336
|
-
* @param {any} [def=undefined] - Default value to return if invalid
|
|
337
|
-
* @returns {Date|undefined} - The parsed Date or default value
|
|
338
|
-
*/
|
|
339
|
-
export function getDateTypeOfISODateTime(str, def = undefined) {
|
|
340
|
-
if (!isValidISODateTime(str))
|
|
341
|
-
return def;
|
|
342
|
-
|
|
343
|
-
// If no timezone specified, treat as local time by appending Z
|
|
344
|
-
// (ISO 8601 without timezone is local time, but for consistency we treat as UTC)
|
|
345
|
-
if (!/[Z+-]\d{2}:\d{2}$/i.test(str) && !str.endsWith('Z')) {
|
|
346
|
-
// Try parsing as-is (Date.parse handles both formats)
|
|
347
|
-
const date = new Date(Date.parse(str.replace(' ', 'T')));
|
|
348
|
-
return isNaN(date.getTime()) ? def : date;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return new Date(Date.parse(str));
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Parses an ISO 8601 time string with optional timezone.
|
|
356
|
-
* Returns a Date object (with 1970-01-01 as date) if valid, otherwise undefined.
|
|
357
|
-
*
|
|
358
|
-
* @param {string} str - The time string to parse
|
|
359
|
-
* @param {any} [def=undefined] - Default value to return if invalid
|
|
360
|
-
* @returns {Date|undefined} - The parsed Date or default value
|
|
361
|
-
*/
|
|
362
|
-
export function getDateTypeOfISOTime(str, def = undefined) {
|
|
363
|
-
if (!isValidISOTime(str))
|
|
364
|
-
return def;
|
|
365
|
-
|
|
366
|
-
// Prepend a dummy date for parsing
|
|
367
|
-
const dateTimeStr = CONST_TIME_INSERTDATE + str;
|
|
368
|
-
const date = new Date(Date.parse(dateTimeStr));
|
|
369
|
-
return isNaN(date.getTime()) ? def : date;
|
|
370
|
-
}
|
|
371
|
-
//#endregion
|