@naturalcycles/js-lib 14.90.0 → 14.91.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/localDate.d.ts +8 -3
- package/dist/datetime/localDate.js +37 -17
- package/dist/datetime/localTime.d.ts +6 -1
- package/dist/datetime/localTime.js +30 -9
- package/dist-esm/datetime/localDate.js +37 -17
- package/dist-esm/datetime/localTime.js +30 -9
- package/package.json +1 -1
- package/src/datetime/localDate.ts +47 -17
- package/src/datetime/localTime.ts +33 -9
|
@@ -18,6 +18,11 @@ export declare class LocalDate {
|
|
|
18
18
|
static of(d: LocalDateConfig): LocalDate;
|
|
19
19
|
static parseCompact(d: string): LocalDate;
|
|
20
20
|
static fromDate(d: Date): LocalDate;
|
|
21
|
+
/**
|
|
22
|
+
* Returns null if invalid.
|
|
23
|
+
*/
|
|
24
|
+
static parseOrNull(d: LocalDateConfig): LocalDate | null;
|
|
25
|
+
static isValid(iso: string): boolean;
|
|
21
26
|
static today(): LocalDate;
|
|
22
27
|
static sort(items: LocalDate[], mutate?: boolean, descending?: boolean): LocalDate[];
|
|
23
28
|
static earliestOrUndefined(items: LocalDate[]): LocalDate | undefined;
|
|
@@ -54,9 +59,9 @@ export declare class LocalDate {
|
|
|
54
59
|
subtract(num: number, unit: LocalDateUnit, mutate?: boolean): LocalDate;
|
|
55
60
|
startOf(unit: LocalDateUnit): LocalDate;
|
|
56
61
|
endOf(unit: LocalDateUnit): LocalDate;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
static getYearLength(year: number): number;
|
|
63
|
+
static getMonthLength(year: number, month: number): number;
|
|
64
|
+
static isLeapYear(year: number): boolean;
|
|
60
65
|
clone(): LocalDate;
|
|
61
66
|
/**
|
|
62
67
|
* Converts LocalDate into instance of Date.
|
|
@@ -22,17 +22,15 @@ class LocalDate {
|
|
|
22
22
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
23
23
|
*/
|
|
24
24
|
static of(d) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const [year, month, day] = d.slice(0, 10).split('-').map(Number);
|
|
28
|
-
if (!day || !month || (!year && year !== 0)) {
|
|
25
|
+
const t = this.parseOrNull(d);
|
|
26
|
+
if (t === null) {
|
|
29
27
|
throw new Error(`Cannot parse "${d}" into LocalDate`);
|
|
30
28
|
}
|
|
31
|
-
return
|
|
29
|
+
return t;
|
|
32
30
|
}
|
|
33
31
|
static parseCompact(d) {
|
|
34
32
|
const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number);
|
|
35
|
-
if (!day || !month ||
|
|
33
|
+
if (!day || !month || !year) {
|
|
36
34
|
throw new Error(`Cannot parse "${d}" into LocalDate`);
|
|
37
35
|
}
|
|
38
36
|
return new LocalDate(year, month, day);
|
|
@@ -40,6 +38,28 @@ class LocalDate {
|
|
|
40
38
|
static fromDate(d) {
|
|
41
39
|
return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
|
42
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns null if invalid.
|
|
43
|
+
*/
|
|
44
|
+
static parseOrNull(d) {
|
|
45
|
+
if (d instanceof LocalDate)
|
|
46
|
+
return d;
|
|
47
|
+
// todo: explore more performant options
|
|
48
|
+
const [year, month, day] = d.slice(0, 10).split('-').map(Number);
|
|
49
|
+
if (!year ||
|
|
50
|
+
!month ||
|
|
51
|
+
month < 1 ||
|
|
52
|
+
month > 12 ||
|
|
53
|
+
!day ||
|
|
54
|
+
day < 1 ||
|
|
55
|
+
day > this.getMonthLength(year, month)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return new LocalDate(year, month, day);
|
|
59
|
+
}
|
|
60
|
+
static isValid(iso) {
|
|
61
|
+
return this.parseOrNull(iso) !== null;
|
|
62
|
+
}
|
|
43
63
|
static today() {
|
|
44
64
|
return this.fromDate(new Date());
|
|
45
65
|
}
|
|
@@ -148,22 +168,22 @@ class LocalDate {
|
|
|
148
168
|
let days = this.day - d.day;
|
|
149
169
|
if (d.year < this.year) {
|
|
150
170
|
for (let year = d.year; year < this.year; year++) {
|
|
151
|
-
days +=
|
|
171
|
+
days += LocalDate.getYearLength(year);
|
|
152
172
|
}
|
|
153
173
|
}
|
|
154
174
|
else if (this.year < d.year) {
|
|
155
175
|
for (let year = this.year; year < d.year; year++) {
|
|
156
|
-
days -=
|
|
176
|
+
days -= LocalDate.getYearLength(year);
|
|
157
177
|
}
|
|
158
178
|
}
|
|
159
179
|
if (d.month < this.month) {
|
|
160
180
|
for (let month = d.month; month < this.month; month++) {
|
|
161
|
-
days +=
|
|
181
|
+
days += LocalDate.getMonthLength(this.year, month);
|
|
162
182
|
}
|
|
163
183
|
}
|
|
164
184
|
else if (this.month < d.month) {
|
|
165
185
|
for (let month = this.month; month < d.month; month++) {
|
|
166
|
-
days -=
|
|
186
|
+
days -= LocalDate.getMonthLength(d.year, month);
|
|
167
187
|
}
|
|
168
188
|
}
|
|
169
189
|
return days;
|
|
@@ -180,7 +200,7 @@ class LocalDate {
|
|
|
180
200
|
year += num;
|
|
181
201
|
}
|
|
182
202
|
// check day overflow
|
|
183
|
-
let monLen =
|
|
203
|
+
let monLen = LocalDate.getMonthLength(year, month);
|
|
184
204
|
while (day > monLen) {
|
|
185
205
|
day -= monLen;
|
|
186
206
|
month += 1;
|
|
@@ -188,7 +208,7 @@ class LocalDate {
|
|
|
188
208
|
year += 1;
|
|
189
209
|
month -= 12;
|
|
190
210
|
}
|
|
191
|
-
monLen =
|
|
211
|
+
monLen = LocalDate.getMonthLength(year, month);
|
|
192
212
|
}
|
|
193
213
|
while (day < 1) {
|
|
194
214
|
day += monLen;
|
|
@@ -197,7 +217,7 @@ class LocalDate {
|
|
|
197
217
|
year -= 1;
|
|
198
218
|
month += 12;
|
|
199
219
|
}
|
|
200
|
-
monLen =
|
|
220
|
+
monLen = LocalDate.getMonthLength(year, month);
|
|
201
221
|
}
|
|
202
222
|
// check month overflow
|
|
203
223
|
while (month > 12) {
|
|
@@ -231,19 +251,19 @@ class LocalDate {
|
|
|
231
251
|
if (unit === 'day')
|
|
232
252
|
return this;
|
|
233
253
|
if (unit === 'month')
|
|
234
|
-
return LocalDate.create(this.year, this.month,
|
|
254
|
+
return LocalDate.create(this.year, this.month, LocalDate.getMonthLength(this.year, this.month));
|
|
235
255
|
// year
|
|
236
256
|
return LocalDate.create(this.year, 12, 31);
|
|
237
257
|
}
|
|
238
|
-
|
|
258
|
+
static getYearLength(year) {
|
|
239
259
|
return this.isLeapYear(year) ? 366 : 365;
|
|
240
260
|
}
|
|
241
|
-
|
|
261
|
+
static getMonthLength(year, month) {
|
|
242
262
|
if (month === 2)
|
|
243
263
|
return this.isLeapYear(year) ? 29 : 28;
|
|
244
264
|
return m31.has(month) ? 31 : 30;
|
|
245
265
|
}
|
|
246
|
-
isLeapYear(year) {
|
|
266
|
+
static isLeapYear(year) {
|
|
247
267
|
if (year % 4 !== 0)
|
|
248
268
|
return false;
|
|
249
269
|
if (year % 100 !== 0)
|
|
@@ -20,6 +20,11 @@ export declare class LocalTime {
|
|
|
20
20
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
21
21
|
*/
|
|
22
22
|
static of(d: LocalTimeConfig): LocalTime;
|
|
23
|
+
/**
|
|
24
|
+
* Returns null if invalid
|
|
25
|
+
*/
|
|
26
|
+
static parseOrNull(d: LocalTimeConfig): LocalTime | null;
|
|
27
|
+
static isValid(d: LocalTimeConfig): boolean;
|
|
23
28
|
static unix(ts: UnixTimestamp): LocalTime;
|
|
24
29
|
static now(): LocalTime;
|
|
25
30
|
static fromComponents(c: {
|
|
@@ -68,7 +73,7 @@ export declare class LocalTime {
|
|
|
68
73
|
unix(): UnixTimestamp;
|
|
69
74
|
valueOf(): UnixTimestamp;
|
|
70
75
|
toISO8601(): IsoDateTime;
|
|
71
|
-
toPretty(): IsoDateTime;
|
|
76
|
+
toPretty(seconds?: boolean): IsoDateTime;
|
|
72
77
|
/**
|
|
73
78
|
* Returns e.g: `19840621_1705`
|
|
74
79
|
*/
|
|
@@ -24,21 +24,38 @@ class LocalTime {
|
|
|
24
24
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
25
25
|
*/
|
|
26
26
|
static of(d) {
|
|
27
|
+
const t = this.parseOrNull(d);
|
|
28
|
+
if (t === null) {
|
|
29
|
+
throw new TypeError(`Cannot parse "${d}" into LocalTime`);
|
|
30
|
+
}
|
|
31
|
+
return t;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Returns null if invalid
|
|
35
|
+
*/
|
|
36
|
+
static parseOrNull(d) {
|
|
27
37
|
if (d instanceof LocalTime)
|
|
28
38
|
return d;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
let date;
|
|
40
|
+
if (d instanceof Date) {
|
|
41
|
+
date = d;
|
|
42
|
+
}
|
|
43
|
+
else if (typeof d === 'number') {
|
|
44
|
+
date = new Date(d * 1000);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
date = new Date(d);
|
|
34
48
|
}
|
|
35
|
-
const date = new Date(d);
|
|
36
49
|
// validation
|
|
37
50
|
if (isNaN(date.getDate())) {
|
|
38
|
-
throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
51
|
+
// throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
52
|
+
return null;
|
|
39
53
|
}
|
|
40
54
|
return new LocalTime(date);
|
|
41
55
|
}
|
|
56
|
+
static isValid(d) {
|
|
57
|
+
return this.parseOrNull(d) !== null;
|
|
58
|
+
}
|
|
42
59
|
static unix(ts) {
|
|
43
60
|
return new LocalTime(new Date(ts * 1000));
|
|
44
61
|
}
|
|
@@ -283,8 +300,12 @@ class LocalTime {
|
|
|
283
300
|
toISO8601() {
|
|
284
301
|
return this.$date.toISOString().slice(0, 19);
|
|
285
302
|
}
|
|
286
|
-
toPretty() {
|
|
287
|
-
return this.$date
|
|
303
|
+
toPretty(seconds = true) {
|
|
304
|
+
return this.$date
|
|
305
|
+
.toISOString()
|
|
306
|
+
.slice(0, seconds ? 19 : 16)
|
|
307
|
+
.split('T')
|
|
308
|
+
.join(' ');
|
|
288
309
|
}
|
|
289
310
|
/**
|
|
290
311
|
* Returns e.g: `19840621_1705`
|
|
@@ -19,17 +19,15 @@ export class LocalDate {
|
|
|
19
19
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
20
20
|
*/
|
|
21
21
|
static of(d) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const [year, month, day] = d.slice(0, 10).split('-').map(Number);
|
|
25
|
-
if (!day || !month || (!year && year !== 0)) {
|
|
22
|
+
const t = this.parseOrNull(d);
|
|
23
|
+
if (t === null) {
|
|
26
24
|
throw new Error(`Cannot parse "${d}" into LocalDate`);
|
|
27
25
|
}
|
|
28
|
-
return
|
|
26
|
+
return t;
|
|
29
27
|
}
|
|
30
28
|
static parseCompact(d) {
|
|
31
29
|
const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number);
|
|
32
|
-
if (!day || !month ||
|
|
30
|
+
if (!day || !month || !year) {
|
|
33
31
|
throw new Error(`Cannot parse "${d}" into LocalDate`);
|
|
34
32
|
}
|
|
35
33
|
return new LocalDate(year, month, day);
|
|
@@ -37,6 +35,28 @@ export class LocalDate {
|
|
|
37
35
|
static fromDate(d) {
|
|
38
36
|
return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
|
39
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns null if invalid.
|
|
40
|
+
*/
|
|
41
|
+
static parseOrNull(d) {
|
|
42
|
+
if (d instanceof LocalDate)
|
|
43
|
+
return d;
|
|
44
|
+
// todo: explore more performant options
|
|
45
|
+
const [year, month, day] = d.slice(0, 10).split('-').map(Number);
|
|
46
|
+
if (!year ||
|
|
47
|
+
!month ||
|
|
48
|
+
month < 1 ||
|
|
49
|
+
month > 12 ||
|
|
50
|
+
!day ||
|
|
51
|
+
day < 1 ||
|
|
52
|
+
day > this.getMonthLength(year, month)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return new LocalDate(year, month, day);
|
|
56
|
+
}
|
|
57
|
+
static isValid(iso) {
|
|
58
|
+
return this.parseOrNull(iso) !== null;
|
|
59
|
+
}
|
|
40
60
|
static today() {
|
|
41
61
|
return this.fromDate(new Date());
|
|
42
62
|
}
|
|
@@ -145,22 +165,22 @@ export class LocalDate {
|
|
|
145
165
|
let days = this.day - d.day;
|
|
146
166
|
if (d.year < this.year) {
|
|
147
167
|
for (let year = d.year; year < this.year; year++) {
|
|
148
|
-
days +=
|
|
168
|
+
days += LocalDate.getYearLength(year);
|
|
149
169
|
}
|
|
150
170
|
}
|
|
151
171
|
else if (this.year < d.year) {
|
|
152
172
|
for (let year = this.year; year < d.year; year++) {
|
|
153
|
-
days -=
|
|
173
|
+
days -= LocalDate.getYearLength(year);
|
|
154
174
|
}
|
|
155
175
|
}
|
|
156
176
|
if (d.month < this.month) {
|
|
157
177
|
for (let month = d.month; month < this.month; month++) {
|
|
158
|
-
days +=
|
|
178
|
+
days += LocalDate.getMonthLength(this.year, month);
|
|
159
179
|
}
|
|
160
180
|
}
|
|
161
181
|
else if (this.month < d.month) {
|
|
162
182
|
for (let month = this.month; month < d.month; month++) {
|
|
163
|
-
days -=
|
|
183
|
+
days -= LocalDate.getMonthLength(d.year, month);
|
|
164
184
|
}
|
|
165
185
|
}
|
|
166
186
|
return days;
|
|
@@ -177,7 +197,7 @@ export class LocalDate {
|
|
|
177
197
|
year += num;
|
|
178
198
|
}
|
|
179
199
|
// check day overflow
|
|
180
|
-
let monLen =
|
|
200
|
+
let monLen = LocalDate.getMonthLength(year, month);
|
|
181
201
|
while (day > monLen) {
|
|
182
202
|
day -= monLen;
|
|
183
203
|
month += 1;
|
|
@@ -185,7 +205,7 @@ export class LocalDate {
|
|
|
185
205
|
year += 1;
|
|
186
206
|
month -= 12;
|
|
187
207
|
}
|
|
188
|
-
monLen =
|
|
208
|
+
monLen = LocalDate.getMonthLength(year, month);
|
|
189
209
|
}
|
|
190
210
|
while (day < 1) {
|
|
191
211
|
day += monLen;
|
|
@@ -194,7 +214,7 @@ export class LocalDate {
|
|
|
194
214
|
year -= 1;
|
|
195
215
|
month += 12;
|
|
196
216
|
}
|
|
197
|
-
monLen =
|
|
217
|
+
monLen = LocalDate.getMonthLength(year, month);
|
|
198
218
|
}
|
|
199
219
|
// check month overflow
|
|
200
220
|
while (month > 12) {
|
|
@@ -228,19 +248,19 @@ export class LocalDate {
|
|
|
228
248
|
if (unit === 'day')
|
|
229
249
|
return this;
|
|
230
250
|
if (unit === 'month')
|
|
231
|
-
return LocalDate.create(this.year, this.month,
|
|
251
|
+
return LocalDate.create(this.year, this.month, LocalDate.getMonthLength(this.year, this.month));
|
|
232
252
|
// year
|
|
233
253
|
return LocalDate.create(this.year, 12, 31);
|
|
234
254
|
}
|
|
235
|
-
|
|
255
|
+
static getYearLength(year) {
|
|
236
256
|
return this.isLeapYear(year) ? 366 : 365;
|
|
237
257
|
}
|
|
238
|
-
|
|
258
|
+
static getMonthLength(year, month) {
|
|
239
259
|
if (month === 2)
|
|
240
260
|
return this.isLeapYear(year) ? 29 : 28;
|
|
241
261
|
return m31.has(month) ? 31 : 30;
|
|
242
262
|
}
|
|
243
|
-
isLeapYear(year) {
|
|
263
|
+
static isLeapYear(year) {
|
|
244
264
|
if (year % 4 !== 0)
|
|
245
265
|
return false;
|
|
246
266
|
if (year % 100 !== 0)
|
|
@@ -21,21 +21,38 @@ export class LocalTime {
|
|
|
21
21
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
22
22
|
*/
|
|
23
23
|
static of(d) {
|
|
24
|
+
const t = this.parseOrNull(d);
|
|
25
|
+
if (t === null) {
|
|
26
|
+
throw new TypeError(`Cannot parse "${d}" into LocalTime`);
|
|
27
|
+
}
|
|
28
|
+
return t;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns null if invalid
|
|
32
|
+
*/
|
|
33
|
+
static parseOrNull(d) {
|
|
24
34
|
if (d instanceof LocalTime)
|
|
25
35
|
return d;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
let date;
|
|
37
|
+
if (d instanceof Date) {
|
|
38
|
+
date = d;
|
|
39
|
+
}
|
|
40
|
+
else if (typeof d === 'number') {
|
|
41
|
+
date = new Date(d * 1000);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
date = new Date(d);
|
|
31
45
|
}
|
|
32
|
-
const date = new Date(d);
|
|
33
46
|
// validation
|
|
34
47
|
if (isNaN(date.getDate())) {
|
|
35
|
-
throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
48
|
+
// throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
49
|
+
return null;
|
|
36
50
|
}
|
|
37
51
|
return new LocalTime(date);
|
|
38
52
|
}
|
|
53
|
+
static isValid(d) {
|
|
54
|
+
return this.parseOrNull(d) !== null;
|
|
55
|
+
}
|
|
39
56
|
static unix(ts) {
|
|
40
57
|
return new LocalTime(new Date(ts * 1000));
|
|
41
58
|
}
|
|
@@ -280,8 +297,12 @@ export class LocalTime {
|
|
|
280
297
|
toISO8601() {
|
|
281
298
|
return this.$date.toISOString().slice(0, 19);
|
|
282
299
|
}
|
|
283
|
-
toPretty() {
|
|
284
|
-
return this.$date
|
|
300
|
+
toPretty(seconds = true) {
|
|
301
|
+
return this.$date
|
|
302
|
+
.toISOString()
|
|
303
|
+
.slice(0, seconds ? 19 : 16)
|
|
304
|
+
.split('T')
|
|
305
|
+
.join(' ');
|
|
285
306
|
}
|
|
286
307
|
/**
|
|
287
308
|
* Returns e.g: `19840621_1705`
|
package/package.json
CHANGED
|
@@ -23,21 +23,19 @@ export class LocalDate {
|
|
|
23
23
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
24
24
|
*/
|
|
25
25
|
static of(d: LocalDateConfig): LocalDate {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const [year, month, day] = d.slice(0, 10).split('-').map(Number)
|
|
26
|
+
const t = this.parseOrNull(d)
|
|
29
27
|
|
|
30
|
-
if (
|
|
28
|
+
if (t === null) {
|
|
31
29
|
throw new Error(`Cannot parse "${d}" into LocalDate`)
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
return
|
|
32
|
+
return t
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
static parseCompact(d: string): LocalDate {
|
|
38
36
|
const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number)
|
|
39
37
|
|
|
40
|
-
if (!day || !month ||
|
|
38
|
+
if (!day || !month || !year) {
|
|
41
39
|
throw new Error(`Cannot parse "${d}" into LocalDate`)
|
|
42
40
|
}
|
|
43
41
|
|
|
@@ -48,6 +46,34 @@ export class LocalDate {
|
|
|
48
46
|
return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate())
|
|
49
47
|
}
|
|
50
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Returns null if invalid.
|
|
51
|
+
*/
|
|
52
|
+
static parseOrNull(d: LocalDateConfig): LocalDate | null {
|
|
53
|
+
if (d instanceof LocalDate) return d
|
|
54
|
+
|
|
55
|
+
// todo: explore more performant options
|
|
56
|
+
const [year, month, day] = d.slice(0, 10).split('-').map(Number)
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
!year ||
|
|
60
|
+
!month ||
|
|
61
|
+
month < 1 ||
|
|
62
|
+
month > 12 ||
|
|
63
|
+
!day ||
|
|
64
|
+
day < 1 ||
|
|
65
|
+
day > this.getMonthLength(year, month)
|
|
66
|
+
) {
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return new LocalDate(year, month, day)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static isValid(iso: string): boolean {
|
|
74
|
+
return this.parseOrNull(iso) !== null
|
|
75
|
+
}
|
|
76
|
+
|
|
51
77
|
static today(): LocalDate {
|
|
52
78
|
return this.fromDate(new Date())
|
|
53
79
|
}
|
|
@@ -203,21 +229,21 @@ export class LocalDate {
|
|
|
203
229
|
|
|
204
230
|
if (d.year < this.year) {
|
|
205
231
|
for (let year = d.year; year < this.year; year++) {
|
|
206
|
-
days +=
|
|
232
|
+
days += LocalDate.getYearLength(year)
|
|
207
233
|
}
|
|
208
234
|
} else if (this.year < d.year) {
|
|
209
235
|
for (let year = this.year; year < d.year; year++) {
|
|
210
|
-
days -=
|
|
236
|
+
days -= LocalDate.getYearLength(year)
|
|
211
237
|
}
|
|
212
238
|
}
|
|
213
239
|
|
|
214
240
|
if (d.month < this.month) {
|
|
215
241
|
for (let month = d.month; month < this.month; month++) {
|
|
216
|
-
days +=
|
|
242
|
+
days += LocalDate.getMonthLength(this.year, month)
|
|
217
243
|
}
|
|
218
244
|
} else if (this.month < d.month) {
|
|
219
245
|
for (let month = this.month; month < d.month; month++) {
|
|
220
|
-
days -=
|
|
246
|
+
days -= LocalDate.getMonthLength(d.year, month)
|
|
221
247
|
}
|
|
222
248
|
}
|
|
223
249
|
|
|
@@ -236,7 +262,7 @@ export class LocalDate {
|
|
|
236
262
|
}
|
|
237
263
|
|
|
238
264
|
// check day overflow
|
|
239
|
-
let monLen =
|
|
265
|
+
let monLen = LocalDate.getMonthLength(year, month)
|
|
240
266
|
while (day > monLen) {
|
|
241
267
|
day -= monLen
|
|
242
268
|
month += 1
|
|
@@ -245,7 +271,7 @@ export class LocalDate {
|
|
|
245
271
|
month -= 12
|
|
246
272
|
}
|
|
247
273
|
|
|
248
|
-
monLen =
|
|
274
|
+
monLen = LocalDate.getMonthLength(year, month)
|
|
249
275
|
}
|
|
250
276
|
while (day < 1) {
|
|
251
277
|
day += monLen
|
|
@@ -255,7 +281,7 @@ export class LocalDate {
|
|
|
255
281
|
month += 12
|
|
256
282
|
}
|
|
257
283
|
|
|
258
|
-
monLen =
|
|
284
|
+
monLen = LocalDate.getMonthLength(year, month)
|
|
259
285
|
}
|
|
260
286
|
|
|
261
287
|
// check month overflow
|
|
@@ -292,21 +318,25 @@ export class LocalDate {
|
|
|
292
318
|
endOf(unit: LocalDateUnit): LocalDate {
|
|
293
319
|
if (unit === 'day') return this
|
|
294
320
|
if (unit === 'month')
|
|
295
|
-
return LocalDate.create(
|
|
321
|
+
return LocalDate.create(
|
|
322
|
+
this.year,
|
|
323
|
+
this.month,
|
|
324
|
+
LocalDate.getMonthLength(this.year, this.month),
|
|
325
|
+
)
|
|
296
326
|
// year
|
|
297
327
|
return LocalDate.create(this.year, 12, 31)
|
|
298
328
|
}
|
|
299
329
|
|
|
300
|
-
|
|
330
|
+
static getYearLength(year: number): number {
|
|
301
331
|
return this.isLeapYear(year) ? 366 : 365
|
|
302
332
|
}
|
|
303
333
|
|
|
304
|
-
|
|
334
|
+
static getMonthLength(year: number, month: number): number {
|
|
305
335
|
if (month === 2) return this.isLeapYear(year) ? 29 : 28
|
|
306
336
|
return m31.has(month) ? 31 : 30
|
|
307
337
|
}
|
|
308
338
|
|
|
309
|
-
|
|
339
|
+
static isLeapYear(year: number): boolean {
|
|
310
340
|
if (year % 4 !== 0) return false
|
|
311
341
|
if (year % 100 !== 0) return true
|
|
312
342
|
return year % 400 === 0
|
|
@@ -36,24 +36,44 @@ export class LocalTime {
|
|
|
36
36
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
37
37
|
*/
|
|
38
38
|
static of(d: LocalTimeConfig): LocalTime {
|
|
39
|
-
|
|
40
|
-
if (d instanceof Date) return new LocalTime(d)
|
|
39
|
+
const t = this.parseOrNull(d)
|
|
41
40
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
return new LocalTime(new Date(d * 1000))
|
|
41
|
+
if (t === null) {
|
|
42
|
+
throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
return t
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns null if invalid
|
|
50
|
+
*/
|
|
51
|
+
static parseOrNull(d: LocalTimeConfig): LocalTime | null {
|
|
52
|
+
if (d instanceof LocalTime) return d
|
|
53
|
+
|
|
54
|
+
let date
|
|
55
|
+
|
|
56
|
+
if (d instanceof Date) {
|
|
57
|
+
date = d
|
|
58
|
+
} else if (typeof d === 'number') {
|
|
59
|
+
date = new Date(d * 1000)
|
|
60
|
+
} else {
|
|
61
|
+
date = new Date(d)
|
|
62
|
+
}
|
|
48
63
|
|
|
49
64
|
// validation
|
|
50
65
|
if (isNaN(date.getDate())) {
|
|
51
|
-
throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
66
|
+
// throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
67
|
+
return null
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
return new LocalTime(date)
|
|
55
71
|
}
|
|
56
72
|
|
|
73
|
+
static isValid(d: LocalTimeConfig): boolean {
|
|
74
|
+
return this.parseOrNull(d) !== null
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
static unix(ts: UnixTimestamp): LocalTime {
|
|
58
78
|
return new LocalTime(new Date(ts * 1000))
|
|
59
79
|
}
|
|
@@ -339,8 +359,12 @@ export class LocalTime {
|
|
|
339
359
|
return this.$date.toISOString().slice(0, 19)
|
|
340
360
|
}
|
|
341
361
|
|
|
342
|
-
toPretty(): IsoDateTime {
|
|
343
|
-
return this.$date
|
|
362
|
+
toPretty(seconds = true): IsoDateTime {
|
|
363
|
+
return this.$date
|
|
364
|
+
.toISOString()
|
|
365
|
+
.slice(0, seconds ? 19 : 16)
|
|
366
|
+
.split('T')
|
|
367
|
+
.join(' ')
|
|
344
368
|
}
|
|
345
369
|
|
|
346
370
|
/**
|