@naturalcycles/js-lib 14.243.0 → 14.244.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.js +1 -1
- package/dist/datetime/localDate.d.ts +31 -26
- package/dist/datetime/localDate.js +180 -154
- package/dist/datetime/localTime.d.ts +43 -41
- package/dist/datetime/localTime.js +178 -118
- package/dist/datetime/timeInterval.js +2 -2
- package/dist/env/buildInfo.js +2 -2
- package/dist-esm/datetime/dateInterval.js +1 -1
- package/dist-esm/datetime/localDate.js +180 -154
- package/dist-esm/datetime/localTime.js +178 -118
- package/dist-esm/datetime/timeInterval.js +2 -2
- package/dist-esm/env/buildInfo.js +2 -2
- package/package.json +2 -1
- package/src/datetime/dateInterval.ts +1 -1
- package/src/datetime/localDate.ts +188 -168
- package/src/datetime/localTime.ts +191 -149
- package/src/datetime/timeInterval.ts +2 -5
- package/src/env/buildInfo.ts +2 -2
|
@@ -3,69 +3,70 @@ import { _isTruthy } from '../is.util';
|
|
|
3
3
|
import { Iterable2 } from '../iter/iterable2';
|
|
4
4
|
import { localTime } from './localTime';
|
|
5
5
|
const MDAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
6
|
-
|
|
6
|
+
// Regex is open-ended (no $ at the end) to support e.g Date+Time string to be parsed (time part will be dropped)
|
|
7
|
+
const DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)/;
|
|
7
8
|
/**
|
|
8
9
|
* LocalDate represents a date without time.
|
|
9
10
|
* It is timezone-independent.
|
|
10
11
|
*/
|
|
11
12
|
export class LocalDate {
|
|
12
|
-
constructor(
|
|
13
|
-
this
|
|
14
|
-
this
|
|
15
|
-
this
|
|
13
|
+
constructor(year, month, day) {
|
|
14
|
+
this.year = year;
|
|
15
|
+
this.month = month;
|
|
16
|
+
this.day = day;
|
|
16
17
|
}
|
|
17
18
|
get(unit) {
|
|
18
|
-
return unit === 'year' ? this
|
|
19
|
+
return unit === 'year' ? this.year : unit === 'month' ? this.month : this.day;
|
|
19
20
|
}
|
|
20
21
|
set(unit, v, mutate = false) {
|
|
21
22
|
const t = mutate ? this : this.clone();
|
|
22
23
|
if (unit === 'year') {
|
|
23
|
-
t
|
|
24
|
+
t.year = v;
|
|
24
25
|
}
|
|
25
26
|
else if (unit === 'month') {
|
|
26
|
-
t
|
|
27
|
+
t.month = v;
|
|
27
28
|
}
|
|
28
29
|
else {
|
|
29
|
-
t
|
|
30
|
+
t.day = v;
|
|
30
31
|
}
|
|
31
32
|
return t;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
return
|
|
34
|
+
setYear(v) {
|
|
35
|
+
return this.set('year', v);
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
return
|
|
37
|
+
setMonth(v) {
|
|
38
|
+
return this.set('month', v);
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
-
return
|
|
40
|
+
setDay(v) {
|
|
41
|
+
return this.set('day', v);
|
|
41
42
|
}
|
|
42
|
-
dayOfWeek() {
|
|
43
|
+
get dayOfWeek() {
|
|
43
44
|
return (this.toDate().getDay() || 7);
|
|
44
45
|
}
|
|
45
46
|
isSame(d) {
|
|
46
|
-
d = localDate.
|
|
47
|
-
return this
|
|
47
|
+
d = localDate.from(d);
|
|
48
|
+
return this.day === d.day && this.month === d.month && this.year === d.year;
|
|
48
49
|
}
|
|
49
50
|
isBefore(d, inclusive = false) {
|
|
50
|
-
const r = this.
|
|
51
|
+
const r = this.compare(d);
|
|
51
52
|
return r === -1 || (r === 0 && inclusive);
|
|
52
53
|
}
|
|
53
54
|
isSameOrBefore(d) {
|
|
54
|
-
return this.
|
|
55
|
+
return this.compare(d) <= 0;
|
|
55
56
|
}
|
|
56
57
|
isAfter(d, inclusive = false) {
|
|
57
|
-
const r = this.
|
|
58
|
+
const r = this.compare(d);
|
|
58
59
|
return r === 1 || (r === 0 && inclusive);
|
|
59
60
|
}
|
|
60
61
|
isSameOrAfter(d) {
|
|
61
|
-
return this.
|
|
62
|
+
return this.compare(d) >= 0;
|
|
62
63
|
}
|
|
63
64
|
isBetween(min, max, incl = '[)') {
|
|
64
|
-
let r = this.
|
|
65
|
+
let r = this.compare(min);
|
|
65
66
|
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
|
|
66
67
|
if (r < 0 || (r === 0 && incl[0] === '('))
|
|
67
68
|
return false;
|
|
68
|
-
r = this.
|
|
69
|
+
r = this.compare(max);
|
|
69
70
|
if (r > 0 || (r === 0 && incl[1] === ')'))
|
|
70
71
|
return false;
|
|
71
72
|
return true;
|
|
@@ -80,13 +81,13 @@ export class LocalDate {
|
|
|
80
81
|
* Third argument allows to override "today".
|
|
81
82
|
*/
|
|
82
83
|
isOlderThan(n, unit, today) {
|
|
83
|
-
return this.isBefore(localDate.
|
|
84
|
+
return this.isBefore(localDate.from(today || new Date()).plus(-n, unit));
|
|
84
85
|
}
|
|
85
86
|
/**
|
|
86
87
|
* Checks if this localDate is same or older (<=) than "today" by X units.
|
|
87
88
|
*/
|
|
88
89
|
isSameOrOlderThan(n, unit, today) {
|
|
89
|
-
return this.isSameOrBefore(localDate.
|
|
90
|
+
return this.isSameOrBefore(localDate.from(today || new Date()).plus(-n, unit));
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
93
|
* Checks if this localDate is younger (>) than "today" by X units.
|
|
@@ -98,13 +99,13 @@ export class LocalDate {
|
|
|
98
99
|
* Third argument allows to override "today".
|
|
99
100
|
*/
|
|
100
101
|
isYoungerThan(n, unit, today) {
|
|
101
|
-
return this.isAfter(localDate.
|
|
102
|
+
return this.isAfter(localDate.from(today || new Date()).plus(-n, unit));
|
|
102
103
|
}
|
|
103
104
|
/**
|
|
104
105
|
* Checks if this localDate is same or younger (>=) than "today" by X units.
|
|
105
106
|
*/
|
|
106
107
|
isSameOrYoungerThan(n, unit, today) {
|
|
107
|
-
return this.isSameOrAfter(localDate.
|
|
108
|
+
return this.isSameOrAfter(localDate.from(today || new Date()).plus(-n, unit));
|
|
108
109
|
}
|
|
109
110
|
getAgeInYears(today) {
|
|
110
111
|
return this.getAgeIn('year', today);
|
|
@@ -116,26 +117,26 @@ export class LocalDate {
|
|
|
116
117
|
return this.getAgeIn('day', today);
|
|
117
118
|
}
|
|
118
119
|
getAgeIn(unit, today) {
|
|
119
|
-
return localDate.
|
|
120
|
+
return localDate.from(today || new Date()).diff(this, unit);
|
|
120
121
|
}
|
|
121
122
|
/**
|
|
122
123
|
* Returns 1 if this > d
|
|
123
124
|
* returns 0 if they are equal
|
|
124
125
|
* returns -1 if this < d
|
|
125
126
|
*/
|
|
126
|
-
|
|
127
|
-
d = localDate.
|
|
128
|
-
if (this
|
|
127
|
+
compare(d) {
|
|
128
|
+
d = localDate.from(d);
|
|
129
|
+
if (this.year < d.year)
|
|
129
130
|
return -1;
|
|
130
|
-
if (this
|
|
131
|
+
if (this.year > d.year)
|
|
131
132
|
return 1;
|
|
132
|
-
if (this
|
|
133
|
+
if (this.month < d.month)
|
|
133
134
|
return -1;
|
|
134
|
-
if (this
|
|
135
|
+
if (this.month > d.month)
|
|
135
136
|
return 1;
|
|
136
|
-
if (this
|
|
137
|
+
if (this.day < d.day)
|
|
137
138
|
return -1;
|
|
138
|
-
if (this
|
|
139
|
+
if (this.day > d.day)
|
|
139
140
|
return 1;
|
|
140
141
|
return 0;
|
|
141
142
|
}
|
|
@@ -151,48 +152,48 @@ export class LocalDate {
|
|
|
151
152
|
* a.diff(b) means "a minus b"
|
|
152
153
|
*/
|
|
153
154
|
diff(d, unit) {
|
|
154
|
-
d = localDate.
|
|
155
|
-
const sign = this.
|
|
155
|
+
d = localDate.from(d);
|
|
156
|
+
const sign = this.compare(d);
|
|
156
157
|
if (!sign)
|
|
157
158
|
return 0;
|
|
158
159
|
// Put items in descending order: "big minus small"
|
|
159
160
|
const [big, small] = sign === 1 ? [this, d] : [d, this];
|
|
160
161
|
if (unit === 'year') {
|
|
161
|
-
let years = big
|
|
162
|
-
if (big
|
|
163
|
-
(big
|
|
164
|
-
big
|
|
165
|
-
!(big
|
|
166
|
-
small
|
|
162
|
+
let years = big.year - small.year;
|
|
163
|
+
if (big.month < small.month ||
|
|
164
|
+
(big.month === small.month &&
|
|
165
|
+
big.day < small.day &&
|
|
166
|
+
!(big.day === localDate.getMonthLength(big.year, big.month) &&
|
|
167
|
+
small.day === localDate.getMonthLength(small.year, small.month)))) {
|
|
167
168
|
years--;
|
|
168
169
|
}
|
|
169
170
|
return years * sign || 0;
|
|
170
171
|
}
|
|
171
172
|
if (unit === 'month') {
|
|
172
|
-
let months = (big
|
|
173
|
-
if (big
|
|
174
|
-
const bigMonthLen = localDate.getMonthLength(big
|
|
175
|
-
if (big
|
|
173
|
+
let months = (big.year - small.year) * 12 + (big.month - small.month);
|
|
174
|
+
if (big.day < small.day) {
|
|
175
|
+
const bigMonthLen = localDate.getMonthLength(big.year, big.month);
|
|
176
|
+
if (big.day !== bigMonthLen || small.day < bigMonthLen) {
|
|
176
177
|
months--;
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
return months * sign || 0;
|
|
180
181
|
}
|
|
181
182
|
// unit is 'day' or 'week'
|
|
182
|
-
let days = big
|
|
183
|
+
let days = big.day - small.day;
|
|
183
184
|
// If small date is after 1st of March - next year's "leapness" should be used
|
|
184
|
-
const offsetYear = small
|
|
185
|
-
for (let year = small
|
|
185
|
+
const offsetYear = small.month >= 3 ? 1 : 0;
|
|
186
|
+
for (let year = small.year; year < big.year; year++) {
|
|
186
187
|
days += localDate.getYearLength(year + offsetYear);
|
|
187
188
|
}
|
|
188
|
-
if (small
|
|
189
|
-
for (let month = small
|
|
190
|
-
days += localDate.getMonthLength(big
|
|
189
|
+
if (small.month < big.month) {
|
|
190
|
+
for (let month = small.month; month < big.month; month++) {
|
|
191
|
+
days += localDate.getMonthLength(big.year, month);
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
|
-
else if (big
|
|
194
|
-
for (let month = big
|
|
195
|
-
days -= localDate.getMonthLength(big
|
|
194
|
+
else if (big.month < small.month) {
|
|
195
|
+
for (let month = big.month; month < small.month; month++) {
|
|
196
|
+
days -= localDate.getMonthLength(big.year, month);
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
199
|
if (unit === 'week') {
|
|
@@ -225,68 +226,68 @@ export class LocalDate {
|
|
|
225
226
|
return this.plus(-num, 'year');
|
|
226
227
|
}
|
|
227
228
|
plus(num, unit, mutate = false) {
|
|
228
|
-
let {
|
|
229
|
+
let { day, month, year } = this;
|
|
229
230
|
if (unit === 'week') {
|
|
230
231
|
num *= 7;
|
|
231
232
|
unit = 'day';
|
|
232
233
|
}
|
|
233
234
|
if (unit === 'day') {
|
|
234
|
-
|
|
235
|
+
day += num;
|
|
235
236
|
}
|
|
236
237
|
else if (unit === 'month') {
|
|
237
|
-
|
|
238
|
+
month += num;
|
|
238
239
|
}
|
|
239
240
|
else if (unit === 'year') {
|
|
240
|
-
|
|
241
|
+
year += num;
|
|
241
242
|
}
|
|
242
243
|
// check month overflow
|
|
243
|
-
while (
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
while (month > 12) {
|
|
245
|
+
year += 1;
|
|
246
|
+
month -= 12;
|
|
246
247
|
}
|
|
247
|
-
while (
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
while (month < 1) {
|
|
249
|
+
year -= 1;
|
|
250
|
+
month += 12;
|
|
250
251
|
}
|
|
251
252
|
// check day overflow
|
|
252
253
|
// Applies not only for 'day' unit, but also e.g 2022-05-31 plus 1 month should be 2022-06-30 (not 31!)
|
|
253
|
-
if (
|
|
254
|
-
while (
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
254
|
+
if (day < 1) {
|
|
255
|
+
while (day < 1) {
|
|
256
|
+
month -= 1;
|
|
257
|
+
if (month < 1) {
|
|
258
|
+
year -= 1;
|
|
259
|
+
month += 12;
|
|
259
260
|
}
|
|
260
|
-
|
|
261
|
+
day += localDate.getMonthLength(year, month);
|
|
261
262
|
}
|
|
262
263
|
}
|
|
263
264
|
else {
|
|
264
|
-
let monLen = localDate.getMonthLength(
|
|
265
|
+
let monLen = localDate.getMonthLength(year, month);
|
|
265
266
|
if (unit !== 'day') {
|
|
266
|
-
if (
|
|
267
|
+
if (day > monLen) {
|
|
267
268
|
// Case of 2022-05-31 plus 1 month should be 2022-06-30, not 31
|
|
268
|
-
|
|
269
|
+
day = monLen;
|
|
269
270
|
}
|
|
270
271
|
}
|
|
271
272
|
else {
|
|
272
|
-
while (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
|
|
273
|
+
while (day > monLen) {
|
|
274
|
+
day -= monLen;
|
|
275
|
+
month += 1;
|
|
276
|
+
if (month > 12) {
|
|
277
|
+
year += 1;
|
|
278
|
+
month -= 12;
|
|
278
279
|
}
|
|
279
|
-
monLen = localDate.getMonthLength(
|
|
280
|
+
monLen = localDate.getMonthLength(year, month);
|
|
280
281
|
}
|
|
281
282
|
}
|
|
282
283
|
}
|
|
283
284
|
if (mutate) {
|
|
284
|
-
this
|
|
285
|
-
this
|
|
286
|
-
this
|
|
285
|
+
this.year = year;
|
|
286
|
+
this.month = month;
|
|
287
|
+
this.day = day;
|
|
287
288
|
return this;
|
|
288
289
|
}
|
|
289
|
-
return new LocalDate(
|
|
290
|
+
return new LocalDate(year, month, day);
|
|
290
291
|
}
|
|
291
292
|
minus(num, unit, mutate = false) {
|
|
292
293
|
return this.plus(-num, unit, mutate);
|
|
@@ -295,27 +296,27 @@ export class LocalDate {
|
|
|
295
296
|
if (unit === 'day')
|
|
296
297
|
return this;
|
|
297
298
|
if (unit === 'month')
|
|
298
|
-
return new LocalDate(this
|
|
299
|
+
return new LocalDate(this.year, this.month, 1);
|
|
299
300
|
// year
|
|
300
|
-
return new LocalDate(this
|
|
301
|
+
return new LocalDate(this.year, 1, 1);
|
|
301
302
|
}
|
|
302
303
|
endOf(unit) {
|
|
303
304
|
if (unit === 'day')
|
|
304
305
|
return this;
|
|
305
306
|
if (unit === 'month')
|
|
306
|
-
return new LocalDate(this
|
|
307
|
+
return new LocalDate(this.year, this.month, localDate.getMonthLength(this.year, this.month));
|
|
307
308
|
// year
|
|
308
|
-
return new LocalDate(this
|
|
309
|
+
return new LocalDate(this.year, 12, 31);
|
|
309
310
|
}
|
|
310
311
|
/**
|
|
311
312
|
* Returns how many days are in the current month.
|
|
312
313
|
* E.g 31 for January.
|
|
313
314
|
*/
|
|
314
|
-
daysInMonth() {
|
|
315
|
-
return localDate.getMonthLength(this
|
|
315
|
+
get daysInMonth() {
|
|
316
|
+
return localDate.getMonthLength(this.year, this.month);
|
|
316
317
|
}
|
|
317
318
|
clone() {
|
|
318
|
-
return new LocalDate(this
|
|
319
|
+
return new LocalDate(this.year, this.month, this.day);
|
|
319
320
|
}
|
|
320
321
|
/**
|
|
321
322
|
* Converts LocalDate into instance of Date.
|
|
@@ -324,7 +325,7 @@ export class LocalDate {
|
|
|
324
325
|
* Timezone will match local timezone.
|
|
325
326
|
*/
|
|
326
327
|
toDate() {
|
|
327
|
-
return new Date(this
|
|
328
|
+
return new Date(this.year, this.month - 1, this.day);
|
|
328
329
|
}
|
|
329
330
|
/**
|
|
330
331
|
* Converts LocalDate to Date in UTC timezone.
|
|
@@ -335,9 +336,9 @@ export class LocalDate {
|
|
|
335
336
|
}
|
|
336
337
|
toDateObject() {
|
|
337
338
|
return {
|
|
338
|
-
year: this
|
|
339
|
-
month: this
|
|
340
|
-
day: this
|
|
339
|
+
year: this.year,
|
|
340
|
+
month: this.month,
|
|
341
|
+
day: this.day,
|
|
341
342
|
};
|
|
342
343
|
}
|
|
343
344
|
/**
|
|
@@ -345,16 +346,16 @@ export class LocalDate {
|
|
|
345
346
|
* LocalTime's Date will be in local timezone.
|
|
346
347
|
*/
|
|
347
348
|
toLocalTime() {
|
|
348
|
-
return localTime.
|
|
349
|
+
return localTime.fromDate(this.toDate());
|
|
349
350
|
}
|
|
350
351
|
/**
|
|
351
352
|
* Returns e.g: `1984-06-21`
|
|
352
353
|
*/
|
|
353
354
|
toISODate() {
|
|
354
355
|
return [
|
|
355
|
-
String(this
|
|
356
|
-
String(this
|
|
357
|
-
String(this
|
|
356
|
+
String(this.year).padStart(4, '0'),
|
|
357
|
+
String(this.month).padStart(2, '0'),
|
|
358
|
+
String(this.day).padStart(2, '0'),
|
|
358
359
|
].join('-');
|
|
359
360
|
}
|
|
360
361
|
/**
|
|
@@ -379,9 +380,9 @@ export class LocalDate {
|
|
|
379
380
|
*/
|
|
380
381
|
toStringCompact() {
|
|
381
382
|
return [
|
|
382
|
-
String(this
|
|
383
|
-
String(this
|
|
384
|
-
String(this
|
|
383
|
+
String(this.year).padStart(4, '0'),
|
|
384
|
+
String(this.month).padStart(2, '0'),
|
|
385
|
+
String(this.day).padStart(2, '0'),
|
|
385
386
|
].join('');
|
|
386
387
|
}
|
|
387
388
|
/**
|
|
@@ -393,13 +394,13 @@ export class LocalDate {
|
|
|
393
394
|
/**
|
|
394
395
|
* Returns unix timestamp of 00:00:00 of that date (in UTC, because unix timestamp always reflects UTC).
|
|
395
396
|
*/
|
|
396
|
-
unix() {
|
|
397
|
+
get unix() {
|
|
397
398
|
return Math.floor(this.toDate().valueOf() / 1000);
|
|
398
399
|
}
|
|
399
400
|
/**
|
|
400
401
|
* Same as .unix(), but in milliseconds.
|
|
401
402
|
*/
|
|
402
|
-
unixMillis() {
|
|
403
|
+
get unixMillis() {
|
|
403
404
|
return this.toDate().valueOf();
|
|
404
405
|
}
|
|
405
406
|
toJSON() {
|
|
@@ -413,12 +414,6 @@ export class LocalDate {
|
|
|
413
414
|
}
|
|
414
415
|
}
|
|
415
416
|
class LocalDateFactory {
|
|
416
|
-
/**
|
|
417
|
-
* Create LocalDate from year, month and day components.
|
|
418
|
-
*/
|
|
419
|
-
create(year, month, day) {
|
|
420
|
-
return new LocalDate(year, month, day);
|
|
421
|
-
}
|
|
422
417
|
/**
|
|
423
418
|
* Create LocalDate from LocalDateInput.
|
|
424
419
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
@@ -428,18 +423,16 @@ class LocalDateFactory {
|
|
|
428
423
|
*
|
|
429
424
|
* Will throw if it fails to parse/construct LocalDate.
|
|
430
425
|
*/
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
});
|
|
436
|
-
return t;
|
|
426
|
+
from(input) {
|
|
427
|
+
const ld = this.fromOrNull(input);
|
|
428
|
+
this.assertNotNull(ld, input);
|
|
429
|
+
return ld;
|
|
437
430
|
}
|
|
438
431
|
/**
|
|
439
432
|
* Tries to construct LocalDate from LocalDateInput, returns null otherwise.
|
|
440
433
|
* Does not throw (returns null instead).
|
|
441
434
|
*/
|
|
442
|
-
|
|
435
|
+
fromOrNull(d) {
|
|
443
436
|
if (!d)
|
|
444
437
|
return null;
|
|
445
438
|
if (d instanceof LocalDate)
|
|
@@ -447,12 +440,25 @@ class LocalDateFactory {
|
|
|
447
440
|
if (d instanceof Date) {
|
|
448
441
|
return this.fromDate(d);
|
|
449
442
|
}
|
|
450
|
-
|
|
451
|
-
|
|
443
|
+
if (typeof d === 'string') {
|
|
444
|
+
return this.fromStringOrNull(d);
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
fromString(s) {
|
|
449
|
+
const ld = this.fromStringOrNull(s);
|
|
450
|
+
this.assertNotNull(ld, s);
|
|
451
|
+
return ld;
|
|
452
|
+
}
|
|
453
|
+
fromStringOrNull(s) {
|
|
454
|
+
if (!s)
|
|
455
|
+
return null;
|
|
456
|
+
const m = DATE_REGEX.exec(s);
|
|
457
|
+
if (!m)
|
|
452
458
|
return null;
|
|
453
|
-
const year = Number(
|
|
454
|
-
const month = Number(
|
|
455
|
-
const day = Number(
|
|
459
|
+
const year = Number(m[1]);
|
|
460
|
+
const month = Number(m[2]);
|
|
461
|
+
const day = Number(m[3]);
|
|
456
462
|
if (!year ||
|
|
457
463
|
!month ||
|
|
458
464
|
month < 1 ||
|
|
@@ -468,22 +474,11 @@ class LocalDateFactory {
|
|
|
468
474
|
* Parses "compact iso8601 format", e.g `19840621` into LocalDate.
|
|
469
475
|
* Throws if it fails to do so.
|
|
470
476
|
*/
|
|
471
|
-
|
|
472
|
-
const [year, month, day] = [
|
|
473
|
-
_assert(day && month && year, `Cannot parse "${
|
|
477
|
+
fromCompactString(s) {
|
|
478
|
+
const [year, month, day] = [s.slice(0, 4), s.slice(4, 6), s.slice(6, 8)].map(Number);
|
|
479
|
+
_assert(day && month && year, `Cannot parse compact string "${s}" into LocalDate`);
|
|
474
480
|
return new LocalDate(year, month, day);
|
|
475
481
|
}
|
|
476
|
-
getYearLength(year) {
|
|
477
|
-
return this.isLeapYear(year) ? 366 : 365;
|
|
478
|
-
}
|
|
479
|
-
getMonthLength(year, month) {
|
|
480
|
-
if (month === 2)
|
|
481
|
-
return this.isLeapYear(year) ? 29 : 28;
|
|
482
|
-
return MDAYS[month];
|
|
483
|
-
}
|
|
484
|
-
isLeapYear(year) {
|
|
485
|
-
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
486
|
-
}
|
|
487
482
|
/**
|
|
488
483
|
* Constructs LocalDate from Date.
|
|
489
484
|
* Takes Date as-is, in its timezone - local or UTC.
|
|
@@ -498,11 +493,43 @@ class LocalDateFactory {
|
|
|
498
493
|
fromDateInUTC(d) {
|
|
499
494
|
return new LocalDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
500
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* Create LocalDate from year, month and day components.
|
|
498
|
+
*/
|
|
499
|
+
fromComponents(year, month, day) {
|
|
500
|
+
return new LocalDate(year, month, day);
|
|
501
|
+
}
|
|
502
|
+
fromDateObject(o) {
|
|
503
|
+
const { year, month, day } = o;
|
|
504
|
+
return new LocalDate(year, month, day);
|
|
505
|
+
}
|
|
506
|
+
assertNotNull(ld, input) {
|
|
507
|
+
_assert(ld !== null, `Cannot parse "${input}" into LocalDate`, {
|
|
508
|
+
input,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
getYearLength(year) {
|
|
512
|
+
return this.isLeapYear(year) ? 366 : 365;
|
|
513
|
+
}
|
|
514
|
+
getMonthLength(year, month) {
|
|
515
|
+
if (month === 2)
|
|
516
|
+
return this.isLeapYear(year) ? 29 : 28;
|
|
517
|
+
return MDAYS[month];
|
|
518
|
+
}
|
|
519
|
+
isLeapYear(year) {
|
|
520
|
+
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Returns true if input is valid to create LocalDate.
|
|
524
|
+
*/
|
|
525
|
+
isValid(input) {
|
|
526
|
+
return this.fromOrNull(input) !== null;
|
|
527
|
+
}
|
|
501
528
|
/**
|
|
502
529
|
* Returns true if isoString is a valid iso8601 string like `yyyy-mm-dd`.
|
|
503
530
|
*/
|
|
504
|
-
|
|
505
|
-
return this.
|
|
531
|
+
isValidString(isoString) {
|
|
532
|
+
return this.fromStringOrNull(isoString) !== null;
|
|
506
533
|
}
|
|
507
534
|
/**
|
|
508
535
|
* Creates LocalDate that represents `today` (in local timezone).
|
|
@@ -521,7 +548,7 @@ class LocalDateFactory {
|
|
|
521
548
|
*/
|
|
522
549
|
sort(items, dir = 'asc', mutate = false) {
|
|
523
550
|
const mod = dir === 'desc' ? -1 : 1;
|
|
524
|
-
return (mutate ? items : [...items]).sort((a, b) => a.
|
|
551
|
+
return (mutate ? items : [...items]).sort((a, b) => a.compare(b) * mod);
|
|
525
552
|
}
|
|
526
553
|
/**
|
|
527
554
|
* Returns the earliest (min) LocalDate from the array, or undefined if the array is empty.
|
|
@@ -537,7 +564,7 @@ class LocalDateFactory {
|
|
|
537
564
|
const items2 = items.filter(_isTruthy);
|
|
538
565
|
_assert(items2.length, 'localDate.min called on empty array');
|
|
539
566
|
return items2
|
|
540
|
-
.map(i => this.
|
|
567
|
+
.map(i => this.from(i))
|
|
541
568
|
.reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
|
|
542
569
|
}
|
|
543
570
|
/**
|
|
@@ -553,7 +580,9 @@ class LocalDateFactory {
|
|
|
553
580
|
max(items) {
|
|
554
581
|
const items2 = items.filter(_isTruthy);
|
|
555
582
|
_assert(items2.length, 'localDate.max called on empty array');
|
|
556
|
-
return items2
|
|
583
|
+
return items2
|
|
584
|
+
.map(i => this.from(i))
|
|
585
|
+
.reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
|
|
557
586
|
}
|
|
558
587
|
/**
|
|
559
588
|
* Returns the range (array) of LocalDates between min and max.
|
|
@@ -571,8 +600,8 @@ class LocalDateFactory {
|
|
|
571
600
|
step *= 7;
|
|
572
601
|
stepUnit = 'day';
|
|
573
602
|
}
|
|
574
|
-
const $min = this.
|
|
575
|
-
const $max = this.
|
|
603
|
+
const $min = this.from(min).startOf(stepUnit);
|
|
604
|
+
const $max = this.from(max).startOf(stepUnit);
|
|
576
605
|
let value = $min;
|
|
577
606
|
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
|
|
578
607
|
if (value.isAfter($min, incl[0] === '[')) {
|
|
@@ -599,20 +628,17 @@ class LocalDateFactory {
|
|
|
599
628
|
* Similar to `localDate.orToday`, but that will instead return Today on falsy input.
|
|
600
629
|
*/
|
|
601
630
|
orUndefined(d) {
|
|
602
|
-
return d ? this.
|
|
631
|
+
return d ? this.from(d) : undefined;
|
|
603
632
|
}
|
|
604
633
|
/**
|
|
605
634
|
* Creates a LocalDate from the input, unless it's falsy - then returns localDate.today.
|
|
606
635
|
*/
|
|
607
636
|
orToday(d) {
|
|
608
|
-
return d ? this.
|
|
637
|
+
return d ? this.from(d) : this.today();
|
|
609
638
|
}
|
|
610
639
|
}
|
|
611
640
|
const localDateFactory = new LocalDateFactory();
|
|
612
|
-
|
|
613
|
-
// return localDateFactory.of(d)
|
|
614
|
-
// }, localDateFactory) as LocalDateFn
|
|
615
|
-
export const localDate = localDateFactory.of.bind(localDateFactory);
|
|
641
|
+
export const localDate = localDateFactory.from.bind(localDateFactory);
|
|
616
642
|
// The line below is the blackest of black magic I have ever written in 2024.
|
|
617
643
|
// And probably 2023 as well.
|
|
618
644
|
Object.setPrototypeOf(localDate, localDateFactory);
|