@naturalcycles/js-lib 14.243.1 → 14.245.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/dist/datetime/dateInterval.d.ts +1 -1
- package/dist/datetime/dateInterval.js +1 -1
- package/dist/datetime/localDate.d.ts +75 -51
- package/dist/datetime/localDate.js +256 -200
- package/dist/datetime/localTime.d.ts +88 -56
- package/dist/datetime/localTime.js +277 -176
- package/dist/datetime/timeInterval.d.ts +2 -2
- package/dist/datetime/timeInterval.js +2 -2
- package/dist/env/buildInfo.js +2 -2
- package/dist/error/error.util.d.ts +1 -1
- package/dist/index.d.ts +37 -37
- package/dist/index.js +37 -37
- package/dist/json-schema/from-data/generateJsonSchemaFromData.d.ts +1 -1
- package/dist/json-schema/jsonSchemaBuilder.d.ts +1 -1
- package/dist/object/object.util.d.ts +1 -1
- package/dist/time/time.util.js +4 -2
- package/dist/zod/zod.util.d.ts +1 -1
- package/dist-esm/datetime/dateInterval.js +1 -1
- package/dist-esm/datetime/localDate.js +256 -199
- package/dist-esm/datetime/localTime.js +277 -175
- package/dist-esm/datetime/timeInterval.js +2 -2
- package/dist-esm/decorators/logMethod.decorator.js +1 -1
- package/dist-esm/env/buildInfo.js +2 -2
- package/dist-esm/index.js +37 -37
- package/dist-esm/json-schema/jsonSchemaBuilder.js +1 -1
- package/dist-esm/time/time.util.js +4 -2
- package/package.json +1 -1
- package/src/datetime/dateInterval.ts +2 -2
- package/src/datetime/localDate.ts +259 -215
- package/src/datetime/localTime.ts +277 -209
- package/src/datetime/timeInterval.ts +4 -4
- package/src/decorators/logMethod.decorator.ts +1 -1
- package/src/define.ts +1 -1
- package/src/env/buildInfo.ts +2 -2
- package/src/error/error.util.ts +3 -3
- package/src/http/fetcher.ts +1 -1
- package/src/index.ts +37 -37
- package/src/json-schema/from-data/generateJsonSchemaFromData.ts +1 -1
- package/src/json-schema/jsonSchemaBuilder.ts +2 -2
- package/src/object/object.util.ts +1 -1
- package/src/time/time.util.ts +4 -1
- package/src/zod/zod.util.ts +1 -1
|
@@ -1,75 +1,82 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.localDate = exports.LocalDate = void 0;
|
|
4
|
-
exports.todayString = todayString;
|
|
5
4
|
const assert_1 = require("../error/assert");
|
|
6
5
|
const is_util_1 = require("../is.util");
|
|
7
6
|
const iterable2_1 = require("../iter/iterable2");
|
|
8
7
|
const localTime_1 = require("./localTime");
|
|
9
8
|
const MDAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Regex is open-ended (no $ at the end) to support e.g Date+Time string to be parsed (time part will be dropped)
|
|
11
|
+
*/
|
|
12
|
+
const DATE_REGEX_LOOSE = /^(\d\d\d\d)-(\d\d)-(\d\d)/;
|
|
13
|
+
/**
|
|
14
|
+
* Strict version.
|
|
15
|
+
*/
|
|
16
|
+
const DATE_REGEX_STRICT = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
17
|
+
const COMPACT_DATE_REGEX = /^(\d\d\d\d)(\d\d)(\d\d)$/;
|
|
11
18
|
/**
|
|
12
19
|
* LocalDate represents a date without time.
|
|
13
20
|
* It is timezone-independent.
|
|
14
21
|
*/
|
|
15
22
|
class LocalDate {
|
|
16
|
-
constructor(
|
|
17
|
-
this
|
|
18
|
-
this
|
|
19
|
-
this
|
|
23
|
+
constructor(year, month, day) {
|
|
24
|
+
this.year = year;
|
|
25
|
+
this.month = month;
|
|
26
|
+
this.day = day;
|
|
20
27
|
}
|
|
21
28
|
get(unit) {
|
|
22
|
-
return unit === 'year' ? this
|
|
29
|
+
return unit === 'year' ? this.year : unit === 'month' ? this.month : this.day;
|
|
23
30
|
}
|
|
24
31
|
set(unit, v, mutate = false) {
|
|
25
32
|
const t = mutate ? this : this.clone();
|
|
26
33
|
if (unit === 'year') {
|
|
27
|
-
t
|
|
34
|
+
t.year = v;
|
|
28
35
|
}
|
|
29
36
|
else if (unit === 'month') {
|
|
30
|
-
t
|
|
37
|
+
t.month = v;
|
|
31
38
|
}
|
|
32
39
|
else {
|
|
33
|
-
t
|
|
40
|
+
t.day = v;
|
|
34
41
|
}
|
|
35
42
|
return t;
|
|
36
43
|
}
|
|
37
|
-
|
|
38
|
-
return
|
|
44
|
+
setYear(v) {
|
|
45
|
+
return this.set('year', v);
|
|
39
46
|
}
|
|
40
|
-
|
|
41
|
-
return
|
|
47
|
+
setMonth(v) {
|
|
48
|
+
return this.set('month', v);
|
|
42
49
|
}
|
|
43
|
-
|
|
44
|
-
return
|
|
50
|
+
setDay(v) {
|
|
51
|
+
return this.set('day', v);
|
|
45
52
|
}
|
|
46
|
-
dayOfWeek() {
|
|
53
|
+
get dayOfWeek() {
|
|
47
54
|
return (this.toDate().getDay() || 7);
|
|
48
55
|
}
|
|
49
56
|
isSame(d) {
|
|
50
|
-
d = exports.localDate.
|
|
51
|
-
return this
|
|
57
|
+
d = exports.localDate.fromInput(d);
|
|
58
|
+
return this.day === d.day && this.month === d.month && this.year === d.year;
|
|
52
59
|
}
|
|
53
60
|
isBefore(d, inclusive = false) {
|
|
54
|
-
const r = this.
|
|
61
|
+
const r = this.compare(d);
|
|
55
62
|
return r === -1 || (r === 0 && inclusive);
|
|
56
63
|
}
|
|
57
64
|
isSameOrBefore(d) {
|
|
58
|
-
return this.
|
|
65
|
+
return this.compare(d) <= 0;
|
|
59
66
|
}
|
|
60
67
|
isAfter(d, inclusive = false) {
|
|
61
|
-
const r = this.
|
|
68
|
+
const r = this.compare(d);
|
|
62
69
|
return r === 1 || (r === 0 && inclusive);
|
|
63
70
|
}
|
|
64
71
|
isSameOrAfter(d) {
|
|
65
|
-
return this.
|
|
72
|
+
return this.compare(d) >= 0;
|
|
66
73
|
}
|
|
67
74
|
isBetween(min, max, incl = '[)') {
|
|
68
|
-
let r = this.
|
|
75
|
+
let r = this.compare(min);
|
|
69
76
|
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
|
|
70
77
|
if (r < 0 || (r === 0 && incl[0] === '('))
|
|
71
78
|
return false;
|
|
72
|
-
r = this.
|
|
79
|
+
r = this.compare(max);
|
|
73
80
|
if (r > 0 || (r === 0 && incl[1] === ')'))
|
|
74
81
|
return false;
|
|
75
82
|
return true;
|
|
@@ -84,13 +91,13 @@ class LocalDate {
|
|
|
84
91
|
* Third argument allows to override "today".
|
|
85
92
|
*/
|
|
86
93
|
isOlderThan(n, unit, today) {
|
|
87
|
-
return this.isBefore(exports.localDate.
|
|
94
|
+
return this.isBefore(exports.localDate.fromInput(today || new Date()).plus(-n, unit));
|
|
88
95
|
}
|
|
89
96
|
/**
|
|
90
97
|
* Checks if this localDate is same or older (<=) than "today" by X units.
|
|
91
98
|
*/
|
|
92
99
|
isSameOrOlderThan(n, unit, today) {
|
|
93
|
-
return this.isSameOrBefore(exports.localDate.
|
|
100
|
+
return this.isSameOrBefore(exports.localDate.fromInput(today || new Date()).plus(-n, unit));
|
|
94
101
|
}
|
|
95
102
|
/**
|
|
96
103
|
* Checks if this localDate is younger (>) than "today" by X units.
|
|
@@ -102,13 +109,13 @@ class LocalDate {
|
|
|
102
109
|
* Third argument allows to override "today".
|
|
103
110
|
*/
|
|
104
111
|
isYoungerThan(n, unit, today) {
|
|
105
|
-
return this.isAfter(exports.localDate.
|
|
112
|
+
return this.isAfter(exports.localDate.fromInput(today || new Date()).plus(-n, unit));
|
|
106
113
|
}
|
|
107
114
|
/**
|
|
108
115
|
* Checks if this localDate is same or younger (>=) than "today" by X units.
|
|
109
116
|
*/
|
|
110
117
|
isSameOrYoungerThan(n, unit, today) {
|
|
111
|
-
return this.isSameOrAfter(exports.localDate.
|
|
118
|
+
return this.isSameOrAfter(exports.localDate.fromInput(today || new Date()).plus(-n, unit));
|
|
112
119
|
}
|
|
113
120
|
getAgeInYears(today) {
|
|
114
121
|
return this.getAgeIn('year', today);
|
|
@@ -120,26 +127,26 @@ class LocalDate {
|
|
|
120
127
|
return this.getAgeIn('day', today);
|
|
121
128
|
}
|
|
122
129
|
getAgeIn(unit, today) {
|
|
123
|
-
return exports.localDate.
|
|
130
|
+
return exports.localDate.fromInput(today || new Date()).diff(this, unit);
|
|
124
131
|
}
|
|
125
132
|
/**
|
|
126
133
|
* Returns 1 if this > d
|
|
127
134
|
* returns 0 if they are equal
|
|
128
135
|
* returns -1 if this < d
|
|
129
136
|
*/
|
|
130
|
-
|
|
131
|
-
d = exports.localDate.
|
|
132
|
-
if (this
|
|
137
|
+
compare(d) {
|
|
138
|
+
d = exports.localDate.fromInput(d);
|
|
139
|
+
if (this.year < d.year)
|
|
133
140
|
return -1;
|
|
134
|
-
if (this
|
|
141
|
+
if (this.year > d.year)
|
|
135
142
|
return 1;
|
|
136
|
-
if (this
|
|
143
|
+
if (this.month < d.month)
|
|
137
144
|
return -1;
|
|
138
|
-
if (this
|
|
145
|
+
if (this.month > d.month)
|
|
139
146
|
return 1;
|
|
140
|
-
if (this
|
|
147
|
+
if (this.day < d.day)
|
|
141
148
|
return -1;
|
|
142
|
-
if (this
|
|
149
|
+
if (this.day > d.day)
|
|
143
150
|
return 1;
|
|
144
151
|
return 0;
|
|
145
152
|
}
|
|
@@ -155,48 +162,48 @@ class LocalDate {
|
|
|
155
162
|
* a.diff(b) means "a minus b"
|
|
156
163
|
*/
|
|
157
164
|
diff(d, unit) {
|
|
158
|
-
d = exports.localDate.
|
|
159
|
-
const sign = this.
|
|
165
|
+
d = exports.localDate.fromInput(d);
|
|
166
|
+
const sign = this.compare(d);
|
|
160
167
|
if (!sign)
|
|
161
168
|
return 0;
|
|
162
169
|
// Put items in descending order: "big minus small"
|
|
163
170
|
const [big, small] = sign === 1 ? [this, d] : [d, this];
|
|
164
171
|
if (unit === 'year') {
|
|
165
|
-
let years = big
|
|
166
|
-
if (big
|
|
167
|
-
(big
|
|
168
|
-
big
|
|
169
|
-
!(big
|
|
170
|
-
small
|
|
172
|
+
let years = big.year - small.year;
|
|
173
|
+
if (big.month < small.month ||
|
|
174
|
+
(big.month === small.month &&
|
|
175
|
+
big.day < small.day &&
|
|
176
|
+
!(big.day === exports.localDate.getMonthLength(big.year, big.month) &&
|
|
177
|
+
small.day === exports.localDate.getMonthLength(small.year, small.month)))) {
|
|
171
178
|
years--;
|
|
172
179
|
}
|
|
173
180
|
return years * sign || 0;
|
|
174
181
|
}
|
|
175
182
|
if (unit === 'month') {
|
|
176
|
-
let months = (big
|
|
177
|
-
if (big
|
|
178
|
-
const bigMonthLen = exports.localDate.getMonthLength(big
|
|
179
|
-
if (big
|
|
183
|
+
let months = (big.year - small.year) * 12 + (big.month - small.month);
|
|
184
|
+
if (big.day < small.day) {
|
|
185
|
+
const bigMonthLen = exports.localDate.getMonthLength(big.year, big.month);
|
|
186
|
+
if (big.day !== bigMonthLen || small.day < bigMonthLen) {
|
|
180
187
|
months--;
|
|
181
188
|
}
|
|
182
189
|
}
|
|
183
190
|
return months * sign || 0;
|
|
184
191
|
}
|
|
185
192
|
// unit is 'day' or 'week'
|
|
186
|
-
let days = big
|
|
193
|
+
let days = big.day - small.day;
|
|
187
194
|
// If small date is after 1st of March - next year's "leapness" should be used
|
|
188
|
-
const offsetYear = small
|
|
189
|
-
for (let year = small
|
|
195
|
+
const offsetYear = small.month >= 3 ? 1 : 0;
|
|
196
|
+
for (let year = small.year; year < big.year; year++) {
|
|
190
197
|
days += exports.localDate.getYearLength(year + offsetYear);
|
|
191
198
|
}
|
|
192
|
-
if (small
|
|
193
|
-
for (let month = small
|
|
194
|
-
days += exports.localDate.getMonthLength(big
|
|
199
|
+
if (small.month < big.month) {
|
|
200
|
+
for (let month = small.month; month < big.month; month++) {
|
|
201
|
+
days += exports.localDate.getMonthLength(big.year, month);
|
|
195
202
|
}
|
|
196
203
|
}
|
|
197
|
-
else if (big
|
|
198
|
-
for (let month = big
|
|
199
|
-
days -= exports.localDate.getMonthLength(big
|
|
204
|
+
else if (big.month < small.month) {
|
|
205
|
+
for (let month = big.month; month < small.month; month++) {
|
|
206
|
+
days -= exports.localDate.getMonthLength(big.year, month);
|
|
200
207
|
}
|
|
201
208
|
}
|
|
202
209
|
if (unit === 'week') {
|
|
@@ -229,68 +236,68 @@ class LocalDate {
|
|
|
229
236
|
return this.plus(-num, 'year');
|
|
230
237
|
}
|
|
231
238
|
plus(num, unit, mutate = false) {
|
|
232
|
-
let {
|
|
239
|
+
let { day, month, year } = this;
|
|
233
240
|
if (unit === 'week') {
|
|
234
241
|
num *= 7;
|
|
235
242
|
unit = 'day';
|
|
236
243
|
}
|
|
237
244
|
if (unit === 'day') {
|
|
238
|
-
|
|
245
|
+
day += num;
|
|
239
246
|
}
|
|
240
247
|
else if (unit === 'month') {
|
|
241
|
-
|
|
248
|
+
month += num;
|
|
242
249
|
}
|
|
243
250
|
else if (unit === 'year') {
|
|
244
|
-
|
|
251
|
+
year += num;
|
|
245
252
|
}
|
|
246
253
|
// check month overflow
|
|
247
|
-
while (
|
|
248
|
-
|
|
249
|
-
|
|
254
|
+
while (month > 12) {
|
|
255
|
+
year += 1;
|
|
256
|
+
month -= 12;
|
|
250
257
|
}
|
|
251
|
-
while (
|
|
252
|
-
|
|
253
|
-
|
|
258
|
+
while (month < 1) {
|
|
259
|
+
year -= 1;
|
|
260
|
+
month += 12;
|
|
254
261
|
}
|
|
255
262
|
// check day overflow
|
|
256
263
|
// Applies not only for 'day' unit, but also e.g 2022-05-31 plus 1 month should be 2022-06-30 (not 31!)
|
|
257
|
-
if (
|
|
258
|
-
while (
|
|
259
|
-
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
if (day < 1) {
|
|
265
|
+
while (day < 1) {
|
|
266
|
+
month -= 1;
|
|
267
|
+
if (month < 1) {
|
|
268
|
+
year -= 1;
|
|
269
|
+
month += 12;
|
|
263
270
|
}
|
|
264
|
-
|
|
271
|
+
day += exports.localDate.getMonthLength(year, month);
|
|
265
272
|
}
|
|
266
273
|
}
|
|
267
274
|
else {
|
|
268
|
-
let monLen = exports.localDate.getMonthLength(
|
|
275
|
+
let monLen = exports.localDate.getMonthLength(year, month);
|
|
269
276
|
if (unit !== 'day') {
|
|
270
|
-
if (
|
|
277
|
+
if (day > monLen) {
|
|
271
278
|
// Case of 2022-05-31 plus 1 month should be 2022-06-30, not 31
|
|
272
|
-
|
|
279
|
+
day = monLen;
|
|
273
280
|
}
|
|
274
281
|
}
|
|
275
282
|
else {
|
|
276
|
-
while (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
|
|
283
|
+
while (day > monLen) {
|
|
284
|
+
day -= monLen;
|
|
285
|
+
month += 1;
|
|
286
|
+
if (month > 12) {
|
|
287
|
+
year += 1;
|
|
288
|
+
month -= 12;
|
|
282
289
|
}
|
|
283
|
-
monLen = exports.localDate.getMonthLength(
|
|
290
|
+
monLen = exports.localDate.getMonthLength(year, month);
|
|
284
291
|
}
|
|
285
292
|
}
|
|
286
293
|
}
|
|
287
294
|
if (mutate) {
|
|
288
|
-
this
|
|
289
|
-
this
|
|
290
|
-
this
|
|
295
|
+
this.year = year;
|
|
296
|
+
this.month = month;
|
|
297
|
+
this.day = day;
|
|
291
298
|
return this;
|
|
292
299
|
}
|
|
293
|
-
return new LocalDate(
|
|
300
|
+
return new LocalDate(year, month, day);
|
|
294
301
|
}
|
|
295
302
|
minus(num, unit, mutate = false) {
|
|
296
303
|
return this.plus(-num, unit, mutate);
|
|
@@ -299,27 +306,28 @@ class LocalDate {
|
|
|
299
306
|
if (unit === 'day')
|
|
300
307
|
return this;
|
|
301
308
|
if (unit === 'month')
|
|
302
|
-
return new LocalDate(this
|
|
309
|
+
return new LocalDate(this.year, this.month, 1);
|
|
303
310
|
// year
|
|
304
|
-
return new LocalDate(this
|
|
311
|
+
return new LocalDate(this.year, 1, 1);
|
|
305
312
|
}
|
|
306
313
|
endOf(unit) {
|
|
307
314
|
if (unit === 'day')
|
|
308
315
|
return this;
|
|
309
|
-
if (unit === 'month')
|
|
310
|
-
return new LocalDate(this
|
|
316
|
+
if (unit === 'month') {
|
|
317
|
+
return new LocalDate(this.year, this.month, exports.localDate.getMonthLength(this.year, this.month));
|
|
318
|
+
}
|
|
311
319
|
// year
|
|
312
|
-
return new LocalDate(this
|
|
320
|
+
return new LocalDate(this.year, 12, 31);
|
|
313
321
|
}
|
|
314
322
|
/**
|
|
315
323
|
* Returns how many days are in the current month.
|
|
316
324
|
* E.g 31 for January.
|
|
317
325
|
*/
|
|
318
|
-
daysInMonth() {
|
|
319
|
-
return exports.localDate.getMonthLength(this
|
|
326
|
+
get daysInMonth() {
|
|
327
|
+
return exports.localDate.getMonthLength(this.year, this.month);
|
|
320
328
|
}
|
|
321
329
|
clone() {
|
|
322
|
-
return new LocalDate(this
|
|
330
|
+
return new LocalDate(this.year, this.month, this.day);
|
|
323
331
|
}
|
|
324
332
|
/**
|
|
325
333
|
* Converts LocalDate into instance of Date.
|
|
@@ -328,7 +336,7 @@ class LocalDate {
|
|
|
328
336
|
* Timezone will match local timezone.
|
|
329
337
|
*/
|
|
330
338
|
toDate() {
|
|
331
|
-
return new Date(this
|
|
339
|
+
return new Date(this.year, this.month - 1, this.day);
|
|
332
340
|
}
|
|
333
341
|
/**
|
|
334
342
|
* Converts LocalDate to Date in UTC timezone.
|
|
@@ -339,9 +347,9 @@ class LocalDate {
|
|
|
339
347
|
}
|
|
340
348
|
toDateObject() {
|
|
341
349
|
return {
|
|
342
|
-
year: this
|
|
343
|
-
month: this
|
|
344
|
-
day: this
|
|
350
|
+
year: this.year,
|
|
351
|
+
month: this.month,
|
|
352
|
+
day: this.day,
|
|
345
353
|
};
|
|
346
354
|
}
|
|
347
355
|
/**
|
|
@@ -349,16 +357,16 @@ class LocalDate {
|
|
|
349
357
|
* LocalTime's Date will be in local timezone.
|
|
350
358
|
*/
|
|
351
359
|
toLocalTime() {
|
|
352
|
-
return localTime_1.localTime.
|
|
360
|
+
return localTime_1.localTime.fromDate(this.toDate());
|
|
353
361
|
}
|
|
354
362
|
/**
|
|
355
363
|
* Returns e.g: `1984-06-21`
|
|
356
364
|
*/
|
|
357
365
|
toISODate() {
|
|
358
366
|
return [
|
|
359
|
-
String(this
|
|
360
|
-
String(this
|
|
361
|
-
String(this
|
|
367
|
+
String(this.year).padStart(4, '0'),
|
|
368
|
+
String(this.month).padStart(2, '0'),
|
|
369
|
+
String(this.day).padStart(2, '0'),
|
|
362
370
|
].join('-');
|
|
363
371
|
}
|
|
364
372
|
/**
|
|
@@ -383,9 +391,9 @@ class LocalDate {
|
|
|
383
391
|
*/
|
|
384
392
|
toStringCompact() {
|
|
385
393
|
return [
|
|
386
|
-
String(this
|
|
387
|
-
String(this
|
|
388
|
-
String(this
|
|
394
|
+
String(this.year).padStart(4, '0'),
|
|
395
|
+
String(this.month).padStart(2, '0'),
|
|
396
|
+
String(this.day).padStart(2, '0'),
|
|
389
397
|
].join('');
|
|
390
398
|
}
|
|
391
399
|
/**
|
|
@@ -397,13 +405,13 @@ class LocalDate {
|
|
|
397
405
|
/**
|
|
398
406
|
* Returns unix timestamp of 00:00:00 of that date (in UTC, because unix timestamp always reflects UTC).
|
|
399
407
|
*/
|
|
400
|
-
unix() {
|
|
408
|
+
get unix() {
|
|
401
409
|
return Math.floor(this.toDate().valueOf() / 1000);
|
|
402
410
|
}
|
|
403
411
|
/**
|
|
404
412
|
* Same as .unix(), but in milliseconds.
|
|
405
413
|
*/
|
|
406
|
-
unixMillis() {
|
|
414
|
+
get unixMillis() {
|
|
407
415
|
return this.toDate().valueOf();
|
|
408
416
|
}
|
|
409
417
|
toJSON() {
|
|
@@ -419,10 +427,36 @@ class LocalDate {
|
|
|
419
427
|
exports.LocalDate = LocalDate;
|
|
420
428
|
class LocalDateFactory {
|
|
421
429
|
/**
|
|
422
|
-
*
|
|
430
|
+
* Creates a LocalDate from the input, unless it's falsy - then returns undefined.
|
|
431
|
+
*
|
|
432
|
+
* Similar to `localDate.orToday`, but that will instead return Today on falsy input.
|
|
423
433
|
*/
|
|
424
|
-
|
|
425
|
-
return
|
|
434
|
+
orUndefined(d) {
|
|
435
|
+
return d ? this.fromInput(d) : undefined;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Creates a LocalDate from the input, unless it's falsy - then returns localDate.today.
|
|
439
|
+
*/
|
|
440
|
+
orToday(d) {
|
|
441
|
+
return d ? this.fromInput(d) : this.today();
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Creates LocalDate that represents `today` (in local timezone).
|
|
445
|
+
*/
|
|
446
|
+
today() {
|
|
447
|
+
return this.fromDate(new Date());
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Creates LocalDate that represents `today` in UTC.
|
|
451
|
+
*/
|
|
452
|
+
todayInUTC() {
|
|
453
|
+
return this.fromDateInUTC(new Date());
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
Convenience function to return current today's IsoDateString representation, e.g `2024-06-21`
|
|
457
|
+
*/
|
|
458
|
+
todayString() {
|
|
459
|
+
return this.fromDate(new Date()).toISODate();
|
|
426
460
|
}
|
|
427
461
|
/**
|
|
428
462
|
* Create LocalDate from LocalDateInput.
|
|
@@ -433,67 +467,112 @@ class LocalDateFactory {
|
|
|
433
467
|
*
|
|
434
468
|
* Will throw if it fails to parse/construct LocalDate.
|
|
435
469
|
*/
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
470
|
+
fromInput(input) {
|
|
471
|
+
if (input instanceof LocalDate)
|
|
472
|
+
return input;
|
|
473
|
+
if (input instanceof Date) {
|
|
474
|
+
return this.fromDate(input);
|
|
475
|
+
}
|
|
476
|
+
// It means it's a string
|
|
477
|
+
return this.fromIsoDateString(input);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Returns true if input is valid to create LocalDate.
|
|
481
|
+
*/
|
|
482
|
+
isValid(input) {
|
|
483
|
+
if (!input)
|
|
484
|
+
return false;
|
|
485
|
+
if (input instanceof LocalDate)
|
|
486
|
+
return true;
|
|
487
|
+
if (input instanceof Date)
|
|
488
|
+
return !isNaN(input.getDate());
|
|
489
|
+
return this.isValidString(input);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Returns true if isoString is a valid iso8601 string like `yyyy-mm-dd`.
|
|
493
|
+
*/
|
|
494
|
+
isValidString(isoString) {
|
|
495
|
+
return !!this.parseToLocalDateOrUndefined(DATE_REGEX_STRICT, isoString);
|
|
442
496
|
}
|
|
443
497
|
/**
|
|
444
|
-
* Tries to
|
|
445
|
-
*
|
|
498
|
+
* Tries to convert/parse the input into LocalDate.
|
|
499
|
+
* Uses LOOSE parsing.
|
|
500
|
+
* If invalid - doesn't throw, but returns undefined instead.
|
|
446
501
|
*/
|
|
447
|
-
|
|
448
|
-
if (!
|
|
449
|
-
return
|
|
450
|
-
if (
|
|
451
|
-
return
|
|
452
|
-
if (
|
|
453
|
-
|
|
502
|
+
try(input) {
|
|
503
|
+
if (!input)
|
|
504
|
+
return;
|
|
505
|
+
if (input instanceof LocalDate)
|
|
506
|
+
return input;
|
|
507
|
+
if (input instanceof Date) {
|
|
508
|
+
if (isNaN(input.getDate()))
|
|
509
|
+
return;
|
|
510
|
+
return new LocalDate(input.getFullYear(), input.getMonth() + 1, input.getDate());
|
|
454
511
|
}
|
|
455
|
-
|
|
512
|
+
return this.parseToLocalDateOrUndefined(DATE_REGEX_LOOSE, input);
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Performs STRICT parsing.
|
|
516
|
+
* Only allows IsoDateString input, nothing else.
|
|
517
|
+
*/
|
|
518
|
+
fromIsoDateString(s) {
|
|
519
|
+
return this.parseToLocalDate(DATE_REGEX_STRICT, s);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Parses "compact iso8601 format", e.g `19840621` into LocalDate.
|
|
523
|
+
* Throws if it fails to do so.
|
|
524
|
+
*/
|
|
525
|
+
fromCompactString(s) {
|
|
526
|
+
return this.parseToLocalDate(COMPACT_DATE_REGEX, s);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Performs LOOSE parsing.
|
|
530
|
+
* Tries to coerce imprefect/incorrect string input into IsoDateString.
|
|
531
|
+
* Use with caution.
|
|
532
|
+
* Allows to input IsoDateTimeString, will drop the Time part of it.
|
|
533
|
+
*/
|
|
534
|
+
parse(s) {
|
|
535
|
+
return this.parseToLocalDate(DATE_REGEX_LOOSE, String(s));
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Throws if it fails to parse the input string via Regex and YMD validation.
|
|
539
|
+
*/
|
|
540
|
+
parseToLocalDate(regex, s) {
|
|
541
|
+
const ld = this.parseToLocalDateOrUndefined(regex, s);
|
|
542
|
+
(0, assert_1._assert)(ld, `Cannot parse "${s}" into LocalDate`);
|
|
543
|
+
return ld;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Tries to parse the input string, returns undefined if input is invalid.
|
|
547
|
+
*/
|
|
548
|
+
parseToLocalDateOrUndefined(regex, s) {
|
|
549
|
+
if (!s || typeof s !== 'string')
|
|
550
|
+
return;
|
|
551
|
+
const m = regex.exec(s);
|
|
456
552
|
if (!m)
|
|
457
|
-
return
|
|
553
|
+
return;
|
|
458
554
|
const year = Number(m[1]);
|
|
459
555
|
const month = Number(m[2]);
|
|
460
556
|
const day = Number(m[3]);
|
|
461
|
-
if (!year
|
|
462
|
-
|
|
463
|
-
month < 1 ||
|
|
464
|
-
month > 12 ||
|
|
465
|
-
!day ||
|
|
466
|
-
day < 1 ||
|
|
467
|
-
day > this.getMonthLength(year, month)) {
|
|
468
|
-
return null;
|
|
469
|
-
}
|
|
557
|
+
if (!this.isDateObjectValid({ year, month, day }))
|
|
558
|
+
return;
|
|
470
559
|
return new LocalDate(year, month, day);
|
|
471
560
|
}
|
|
472
561
|
/**
|
|
473
|
-
*
|
|
474
|
-
* Throws if it fails to do so.
|
|
562
|
+
* Throws on invalid value.
|
|
475
563
|
*/
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
(0, assert_1._assert)(day && month && year, `Cannot parse "${d}" into LocalDate`);
|
|
479
|
-
return new LocalDate(year, month, day);
|
|
480
|
-
}
|
|
481
|
-
getYearLength(year) {
|
|
482
|
-
return this.isLeapYear(year) ? 366 : 365;
|
|
483
|
-
}
|
|
484
|
-
getMonthLength(year, month) {
|
|
485
|
-
if (month === 2)
|
|
486
|
-
return this.isLeapYear(year) ? 29 : 28;
|
|
487
|
-
return MDAYS[month];
|
|
564
|
+
validateDateObject(o) {
|
|
565
|
+
(0, assert_1._assert)(this.isDateObjectValid(o), `Cannot construct LocalDate from: ${o.year}-${o.month}-${o.day}`);
|
|
488
566
|
}
|
|
489
|
-
|
|
490
|
-
return year
|
|
567
|
+
isDateObjectValid({ year, month, day }) {
|
|
568
|
+
return (!!year && month >= 1 && month <= 12 && day >= 1 && day <= this.getMonthLength(year, month));
|
|
491
569
|
}
|
|
492
570
|
/**
|
|
493
571
|
* Constructs LocalDate from Date.
|
|
494
572
|
* Takes Date as-is, in its timezone - local or UTC.
|
|
495
573
|
*/
|
|
496
574
|
fromDate(d) {
|
|
575
|
+
(0, assert_1._assert)(!isNaN(d.getDate()), `localDate.fromDate is called on Date object that is invalid`);
|
|
497
576
|
return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
|
498
577
|
}
|
|
499
578
|
/**
|
|
@@ -501,32 +580,19 @@ class LocalDateFactory {
|
|
|
501
580
|
* Takes Date's year/month/day components in UTC, using getUTCFullYear, getUTCMonth, getUTCDate.
|
|
502
581
|
*/
|
|
503
582
|
fromDateInUTC(d) {
|
|
583
|
+
(0, assert_1._assert)(!isNaN(d.getDate()), `localDate.fromDateInUTC is called on Date object that is invalid`);
|
|
504
584
|
return new LocalDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
505
585
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
isValid(isoString) {
|
|
510
|
-
return this.parseOrNull(isoString) !== null;
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* Creates LocalDate that represents `today` (in local timezone).
|
|
514
|
-
*/
|
|
515
|
-
today() {
|
|
516
|
-
return this.fromDate(new Date());
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Creates LocalDate that represents `today` in UTC.
|
|
520
|
-
*/
|
|
521
|
-
todayInUTC() {
|
|
522
|
-
return this.fromDateInUTC(new Date());
|
|
586
|
+
fromDateObject(o) {
|
|
587
|
+
this.validateDateObject(o);
|
|
588
|
+
return new LocalDate(o.year, o.month, o.day);
|
|
523
589
|
}
|
|
524
590
|
/**
|
|
525
591
|
* Sorts an array of LocalDates in `dir` order (ascending by default).
|
|
526
592
|
*/
|
|
527
593
|
sort(items, dir = 'asc', mutate = false) {
|
|
528
594
|
const mod = dir === 'desc' ? -1 : 1;
|
|
529
|
-
return (mutate ? items : [...items]).sort((a, b) => a.
|
|
595
|
+
return (mutate ? items : [...items]).sort((a, b) => a.compare(b) * mod);
|
|
530
596
|
}
|
|
531
597
|
/**
|
|
532
598
|
* Returns the earliest (min) LocalDate from the array, or undefined if the array is empty.
|
|
@@ -542,7 +608,7 @@ class LocalDateFactory {
|
|
|
542
608
|
const items2 = items.filter(is_util_1._isTruthy);
|
|
543
609
|
(0, assert_1._assert)(items2.length, 'localDate.min called on empty array');
|
|
544
610
|
return items2
|
|
545
|
-
.map(i => this.
|
|
611
|
+
.map(i => this.fromInput(i))
|
|
546
612
|
.reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
|
|
547
613
|
}
|
|
548
614
|
/**
|
|
@@ -558,7 +624,9 @@ class LocalDateFactory {
|
|
|
558
624
|
max(items) {
|
|
559
625
|
const items2 = items.filter(is_util_1._isTruthy);
|
|
560
626
|
(0, assert_1._assert)(items2.length, 'localDate.max called on empty array');
|
|
561
|
-
return items2
|
|
627
|
+
return items2
|
|
628
|
+
.map(i => this.fromInput(i))
|
|
629
|
+
.reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
|
|
562
630
|
}
|
|
563
631
|
/**
|
|
564
632
|
* Returns the range (array) of LocalDates between min and max.
|
|
@@ -576,8 +644,8 @@ class LocalDateFactory {
|
|
|
576
644
|
step *= 7;
|
|
577
645
|
stepUnit = 'day';
|
|
578
646
|
}
|
|
579
|
-
const $min = this.
|
|
580
|
-
const $max = this.
|
|
647
|
+
const $min = this.fromInput(min).startOf(stepUnit);
|
|
648
|
+
const $max = this.fromInput(max).startOf(stepUnit);
|
|
581
649
|
let value = $min;
|
|
582
650
|
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
|
|
583
651
|
if (value.isAfter($min, incl[0] === '[')) {
|
|
@@ -598,32 +666,20 @@ class LocalDateFactory {
|
|
|
598
666
|
},
|
|
599
667
|
});
|
|
600
668
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
*
|
|
604
|
-
* Similar to `localDate.orToday`, but that will instead return Today on falsy input.
|
|
605
|
-
*/
|
|
606
|
-
orUndefined(d) {
|
|
607
|
-
return d ? this.of(d) : undefined;
|
|
669
|
+
getYearLength(year) {
|
|
670
|
+
return this.isLeapYear(year) ? 366 : 365;
|
|
608
671
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
672
|
+
getMonthLength(year, month) {
|
|
673
|
+
if (month === 2)
|
|
674
|
+
return this.isLeapYear(year) ? 29 : 28;
|
|
675
|
+
return MDAYS[month];
|
|
676
|
+
}
|
|
677
|
+
isLeapYear(year) {
|
|
678
|
+
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
614
679
|
}
|
|
615
680
|
}
|
|
616
681
|
const localDateFactory = new LocalDateFactory();
|
|
617
|
-
|
|
618
|
-
// return localDateFactory.of(d)
|
|
619
|
-
// }, localDateFactory) as LocalDateFn
|
|
620
|
-
exports.localDate = localDateFactory.of.bind(localDateFactory);
|
|
682
|
+
exports.localDate = localDateFactory.fromInput.bind(localDateFactory);
|
|
621
683
|
// The line below is the blackest of black magic I have ever written in 2024.
|
|
622
684
|
// And probably 2023 as well.
|
|
623
685
|
Object.setPrototypeOf(exports.localDate, localDateFactory);
|
|
624
|
-
/**
|
|
625
|
-
Convenience function to return current today's IsoDateString representation, e.g `2024-06-21`
|
|
626
|
-
*/
|
|
627
|
-
function todayString() {
|
|
628
|
-
return exports.localDate.fromDate(new Date()).toISODate();
|
|
629
|
-
}
|