@naturalcycles/js-lib 14.99.2 → 14.100.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.js +37 -22
- package/dist/datetime/localTime.d.ts +4 -0
- package/dist/datetime/localTime.js +55 -59
- package/dist-esm/datetime/localDate.js +37 -22
- package/dist-esm/datetime/localTime.js +56 -59
- package/package.json +1 -1
- package/src/datetime/localDate.ts +42 -23
- package/src/datetime/localTime.ts +68 -65
|
@@ -218,15 +218,23 @@ class LocalDate {
|
|
|
218
218
|
const [big, small] = sign === 1 ? [this, d] : [d, this];
|
|
219
219
|
if (unit === 'year') {
|
|
220
220
|
let years = big.$year - small.$year;
|
|
221
|
-
if (big.$month < small.$month ||
|
|
221
|
+
if (big.$month < small.$month ||
|
|
222
|
+
(big.$month === small.$month &&
|
|
223
|
+
big.$day < small.$day &&
|
|
224
|
+
!(big.$day === LocalDate.getMonthLength(big.$year, big.$month) &&
|
|
225
|
+
small.$day === LocalDate.getMonthLength(small.$year, small.$month)))) {
|
|
222
226
|
years--;
|
|
223
227
|
}
|
|
224
228
|
return years * sign || 0;
|
|
225
229
|
}
|
|
226
230
|
if (unit === 'month') {
|
|
227
231
|
let months = (big.$year - small.$year) * 12 + (big.$month - small.$month);
|
|
228
|
-
if (big.$day < small.$day)
|
|
229
|
-
|
|
232
|
+
if (big.$day < small.$day) {
|
|
233
|
+
const bigMonthLen = LocalDate.getMonthLength(big.$year, big.$month);
|
|
234
|
+
if (big.$day !== bigMonthLen || small.$day < bigMonthLen) {
|
|
235
|
+
months--;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
230
238
|
return months * sign || 0;
|
|
231
239
|
}
|
|
232
240
|
// unit is 'day' or 'week'
|
|
@@ -266,20 +274,36 @@ class LocalDate {
|
|
|
266
274
|
else if (unit === 'year') {
|
|
267
275
|
$year += num;
|
|
268
276
|
}
|
|
277
|
+
// check month overflow
|
|
278
|
+
while ($month > 12) {
|
|
279
|
+
$year += 1;
|
|
280
|
+
$month -= 12;
|
|
281
|
+
}
|
|
282
|
+
while ($month < 1) {
|
|
283
|
+
$year -= 1;
|
|
284
|
+
$month += 12;
|
|
285
|
+
}
|
|
269
286
|
// check day overflow
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
287
|
+
// Applies not only for 'day' unit, but also e.g 2022-05-31 plus 1 month should be 2022-06-30 (not 31!)
|
|
288
|
+
if ($day < 1) {
|
|
289
|
+
while ($day < 1) {
|
|
290
|
+
$month -= 1;
|
|
291
|
+
if ($month < 1) {
|
|
292
|
+
$year -= 1;
|
|
293
|
+
$month += 12;
|
|
294
|
+
}
|
|
295
|
+
$day += LocalDate.getMonthLength($year, $month);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
let monLen = LocalDate.getMonthLength($year, $month);
|
|
300
|
+
if (unit !== 'day') {
|
|
301
|
+
if ($day > monLen) {
|
|
302
|
+
// Case of 2022-05-31 plus 1 month should be 2022-06-30, not 31
|
|
303
|
+
$day = monLen;
|
|
279
304
|
}
|
|
280
305
|
}
|
|
281
306
|
else {
|
|
282
|
-
let monLen = LocalDate.getMonthLength($year, $month);
|
|
283
307
|
while ($day > monLen) {
|
|
284
308
|
$day -= monLen;
|
|
285
309
|
$month += 1;
|
|
@@ -291,15 +315,6 @@ class LocalDate {
|
|
|
291
315
|
}
|
|
292
316
|
}
|
|
293
317
|
}
|
|
294
|
-
// check month overflow
|
|
295
|
-
while ($month > 12) {
|
|
296
|
-
$year += 1;
|
|
297
|
-
$month -= 12;
|
|
298
|
-
}
|
|
299
|
-
while ($month < 1) {
|
|
300
|
-
$year -= 1;
|
|
301
|
-
$month += 12;
|
|
302
|
-
}
|
|
303
318
|
if (mutate) {
|
|
304
319
|
this.$year = $year;
|
|
305
320
|
this.$month = $month;
|
|
@@ -31,6 +31,10 @@ export declare class LocalTime {
|
|
|
31
31
|
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
32
32
|
*/
|
|
33
33
|
static of(d: LocalTimeConfig): LocalTime;
|
|
34
|
+
/**
|
|
35
|
+
* Create LocalTime from unixTimestamp in milliseconds (not in seconds).
|
|
36
|
+
*/
|
|
37
|
+
static ofMillis(millis: number): LocalTime;
|
|
34
38
|
/**
|
|
35
39
|
* Returns null if invalid
|
|
36
40
|
*/
|
|
@@ -39,6 +39,12 @@ class LocalTime {
|
|
|
39
39
|
}
|
|
40
40
|
return t;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Create LocalTime from unixTimestamp in milliseconds (not in seconds).
|
|
44
|
+
*/
|
|
45
|
+
static ofMillis(millis) {
|
|
46
|
+
return LocalTime.of(new Date(millis));
|
|
47
|
+
}
|
|
42
48
|
/**
|
|
43
49
|
* Returns null if invalid
|
|
44
50
|
*/
|
|
@@ -96,7 +102,7 @@ class LocalTime {
|
|
|
96
102
|
return new LocalTime(new Date());
|
|
97
103
|
}
|
|
98
104
|
static fromComponents(c) {
|
|
99
|
-
return new LocalTime(new Date(c.year, c.month - 1, c.day, c.hour, c.minute, c.second));
|
|
105
|
+
return new LocalTime(new Date(c.year, c.month - 1, c.day || 1, c.hour || 0, c.minute || 0, c.second || 0));
|
|
100
106
|
}
|
|
101
107
|
get(unit) {
|
|
102
108
|
if (unit === 'year') {
|
|
@@ -177,14 +183,9 @@ class LocalTime {
|
|
|
177
183
|
}
|
|
178
184
|
setComponents(c, mutate = false) {
|
|
179
185
|
const d = mutate ? this.$date : new Date(this.$date);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (c.month) {
|
|
184
|
-
d.setMonth(c.month - 1);
|
|
185
|
-
}
|
|
186
|
-
if (c.day) {
|
|
187
|
-
d.setDate(c.day);
|
|
186
|
+
// Year, month and day set all-at-once, to avoid 30/31 (and 28/29) mishap
|
|
187
|
+
if (c.day || c.month !== undefined || c.year !== undefined) {
|
|
188
|
+
d.setFullYear(c.year ?? d.getFullYear(), c.month ? c.month - 1 : d.getMonth(), c.day || d.getDate());
|
|
188
189
|
}
|
|
189
190
|
if (c.hour !== undefined) {
|
|
190
191
|
d.setHours(c.hour);
|
|
@@ -202,6 +203,10 @@ class LocalTime {
|
|
|
202
203
|
num *= 7;
|
|
203
204
|
unit = 'day';
|
|
204
205
|
}
|
|
206
|
+
if (unit === 'year' || unit === 'month') {
|
|
207
|
+
const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate);
|
|
208
|
+
return mutate ? this : LocalTime.of(d);
|
|
209
|
+
}
|
|
205
210
|
return this.set(unit, this.get(unit) + num, mutate);
|
|
206
211
|
}
|
|
207
212
|
subtract(num, unit, mutate = false) {
|
|
@@ -215,33 +220,14 @@ class LocalTime {
|
|
|
215
220
|
const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000;
|
|
216
221
|
if (!secDiff)
|
|
217
222
|
return 0;
|
|
218
|
-
if (unit === 'year' || unit === 'month') {
|
|
219
|
-
const sign = secDiff > 0 ? 1 : -1;
|
|
220
|
-
// Put items in descending order: "big minus small"
|
|
221
|
-
const [big, small] = sign === 1 ? [this.$date, date2] : [date2, this.$date];
|
|
222
|
-
if (unit === 'year') {
|
|
223
|
-
let years = big.getFullYear() - small.getFullYear();
|
|
224
|
-
const big2 = new Date(big);
|
|
225
|
-
const small2 = new Date(small);
|
|
226
|
-
big2.setFullYear(1584);
|
|
227
|
-
small2.setFullYear(1584);
|
|
228
|
-
if (big2 < small2)
|
|
229
|
-
years--;
|
|
230
|
-
return years * sign || 0;
|
|
231
|
-
}
|
|
232
|
-
if (unit === 'month') {
|
|
233
|
-
let months = (big.getFullYear() - small.getFullYear()) * 12 + big.getMonth() - small.getMonth();
|
|
234
|
-
const big2 = new Date(big);
|
|
235
|
-
const small2 = new Date(small);
|
|
236
|
-
big2.setFullYear(1584, 0);
|
|
237
|
-
small2.setFullYear(1584, 0);
|
|
238
|
-
if (big2 < small2)
|
|
239
|
-
months--;
|
|
240
|
-
return months * sign || 0;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
223
|
let r;
|
|
244
|
-
if (unit === '
|
|
224
|
+
if (unit === 'year') {
|
|
225
|
+
r = differenceInMonths(this.getDate(), date2) / 12;
|
|
226
|
+
}
|
|
227
|
+
else if (unit === 'month') {
|
|
228
|
+
r = differenceInMonths(this.getDate(), date2);
|
|
229
|
+
}
|
|
230
|
+
else if (unit === 'day') {
|
|
245
231
|
r = secDiff / SECONDS_IN_DAY;
|
|
246
232
|
}
|
|
247
233
|
else if (unit === 'week') {
|
|
@@ -486,7 +472,7 @@ class LocalTime {
|
|
|
486
472
|
].join('');
|
|
487
473
|
}
|
|
488
474
|
toString() {
|
|
489
|
-
return
|
|
475
|
+
return this.toISODateTime();
|
|
490
476
|
}
|
|
491
477
|
toJSON() {
|
|
492
478
|
return this.unix();
|
|
@@ -543,29 +529,6 @@ function getWeekYear(date) {
|
|
|
543
529
|
return year - 1;
|
|
544
530
|
}
|
|
545
531
|
}
|
|
546
|
-
// function setWeekYear(
|
|
547
|
-
// date: Date,
|
|
548
|
-
// year: number,
|
|
549
|
-
// ): Date {
|
|
550
|
-
// const diff = differenceInCalendarDays(date, startOfWeekYear(date))
|
|
551
|
-
// const fourthOfJanuary = new Date(0)
|
|
552
|
-
// fourthOfJanuary.setFullYear(year, 0, 4)
|
|
553
|
-
// fourthOfJanuary.setHours(0, 0, 0, 0)
|
|
554
|
-
// date = startOfWeekYear(fourthOfJanuary)
|
|
555
|
-
// date.setDate(date.getDate() + diff)
|
|
556
|
-
// return date
|
|
557
|
-
// }
|
|
558
|
-
// function differenceInCalendarDays(
|
|
559
|
-
// dateLeft: Date,
|
|
560
|
-
// dateRight: Date,
|
|
561
|
-
// ): number {
|
|
562
|
-
// return Math.round((startOfDay(dateLeft).getTime() - startOfDay(dateRight).getTime()) / MILLISECONDS_IN_DAY)
|
|
563
|
-
// }
|
|
564
|
-
// function startOfDay(date: Date, mutate = false): Date {
|
|
565
|
-
// const d = mutate ? date : new Date(date)
|
|
566
|
-
// d.setHours(0, 0, 0, 0)
|
|
567
|
-
// return d
|
|
568
|
-
// }
|
|
569
532
|
// based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
|
|
570
533
|
function startOfWeek(date, mutate = false) {
|
|
571
534
|
const d = mutate ? date : new Date(date);
|
|
@@ -583,3 +546,36 @@ function endOfWeek(date, mutate = false) {
|
|
|
583
546
|
d.setDate(d.getDate() + diff);
|
|
584
547
|
return d;
|
|
585
548
|
}
|
|
549
|
+
function addMonths(d, num, mutate = false) {
|
|
550
|
+
if (!mutate)
|
|
551
|
+
d = new Date(d);
|
|
552
|
+
let day = d.getDate();
|
|
553
|
+
let month = d.getMonth() + 1 + num;
|
|
554
|
+
if (day < 29) {
|
|
555
|
+
d.setMonth(month - 1);
|
|
556
|
+
return d;
|
|
557
|
+
}
|
|
558
|
+
let year = d.getFullYear();
|
|
559
|
+
while (month > 12) {
|
|
560
|
+
year++;
|
|
561
|
+
month -= 12;
|
|
562
|
+
}
|
|
563
|
+
while (month < 1) {
|
|
564
|
+
year--;
|
|
565
|
+
month += 12;
|
|
566
|
+
}
|
|
567
|
+
const monthLen = localDate_1.LocalDate.getMonthLength(year, month);
|
|
568
|
+
if (day > monthLen)
|
|
569
|
+
day = monthLen;
|
|
570
|
+
d.setFullYear(year, month - 1, day);
|
|
571
|
+
return d;
|
|
572
|
+
}
|
|
573
|
+
function differenceInMonths(a, b) {
|
|
574
|
+
if (a.getDate() < b.getDate())
|
|
575
|
+
return -differenceInMonths(b, a);
|
|
576
|
+
const wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth());
|
|
577
|
+
const anchor = addMonths(a, wholeMonthDiff).getTime();
|
|
578
|
+
const sign = b.getTime() - anchor >= 0 ? 1 : -1;
|
|
579
|
+
const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime();
|
|
580
|
+
return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign);
|
|
581
|
+
}
|
|
@@ -215,15 +215,23 @@ export class LocalDate {
|
|
|
215
215
|
const [big, small] = sign === 1 ? [this, d] : [d, this];
|
|
216
216
|
if (unit === 'year') {
|
|
217
217
|
let years = big.$year - small.$year;
|
|
218
|
-
if (big.$month < small.$month ||
|
|
218
|
+
if (big.$month < small.$month ||
|
|
219
|
+
(big.$month === small.$month &&
|
|
220
|
+
big.$day < small.$day &&
|
|
221
|
+
!(big.$day === LocalDate.getMonthLength(big.$year, big.$month) &&
|
|
222
|
+
small.$day === LocalDate.getMonthLength(small.$year, small.$month)))) {
|
|
219
223
|
years--;
|
|
220
224
|
}
|
|
221
225
|
return years * sign || 0;
|
|
222
226
|
}
|
|
223
227
|
if (unit === 'month') {
|
|
224
228
|
let months = (big.$year - small.$year) * 12 + (big.$month - small.$month);
|
|
225
|
-
if (big.$day < small.$day)
|
|
226
|
-
|
|
229
|
+
if (big.$day < small.$day) {
|
|
230
|
+
const bigMonthLen = LocalDate.getMonthLength(big.$year, big.$month);
|
|
231
|
+
if (big.$day !== bigMonthLen || small.$day < bigMonthLen) {
|
|
232
|
+
months--;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
227
235
|
return months * sign || 0;
|
|
228
236
|
}
|
|
229
237
|
// unit is 'day' or 'week'
|
|
@@ -263,20 +271,36 @@ export class LocalDate {
|
|
|
263
271
|
else if (unit === 'year') {
|
|
264
272
|
$year += num;
|
|
265
273
|
}
|
|
274
|
+
// check month overflow
|
|
275
|
+
while ($month > 12) {
|
|
276
|
+
$year += 1;
|
|
277
|
+
$month -= 12;
|
|
278
|
+
}
|
|
279
|
+
while ($month < 1) {
|
|
280
|
+
$year -= 1;
|
|
281
|
+
$month += 12;
|
|
282
|
+
}
|
|
266
283
|
// check day overflow
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
284
|
+
// Applies not only for 'day' unit, but also e.g 2022-05-31 plus 1 month should be 2022-06-30 (not 31!)
|
|
285
|
+
if ($day < 1) {
|
|
286
|
+
while ($day < 1) {
|
|
287
|
+
$month -= 1;
|
|
288
|
+
if ($month < 1) {
|
|
289
|
+
$year -= 1;
|
|
290
|
+
$month += 12;
|
|
291
|
+
}
|
|
292
|
+
$day += LocalDate.getMonthLength($year, $month);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
let monLen = LocalDate.getMonthLength($year, $month);
|
|
297
|
+
if (unit !== 'day') {
|
|
298
|
+
if ($day > monLen) {
|
|
299
|
+
// Case of 2022-05-31 plus 1 month should be 2022-06-30, not 31
|
|
300
|
+
$day = monLen;
|
|
276
301
|
}
|
|
277
302
|
}
|
|
278
303
|
else {
|
|
279
|
-
let monLen = LocalDate.getMonthLength($year, $month);
|
|
280
304
|
while ($day > monLen) {
|
|
281
305
|
$day -= monLen;
|
|
282
306
|
$month += 1;
|
|
@@ -288,15 +312,6 @@ export class LocalDate {
|
|
|
288
312
|
}
|
|
289
313
|
}
|
|
290
314
|
}
|
|
291
|
-
// check month overflow
|
|
292
|
-
while ($month > 12) {
|
|
293
|
-
$year += 1;
|
|
294
|
-
$month -= 12;
|
|
295
|
-
}
|
|
296
|
-
while ($month < 1) {
|
|
297
|
-
$year -= 1;
|
|
298
|
-
$month += 12;
|
|
299
|
-
}
|
|
300
315
|
if (mutate) {
|
|
301
316
|
this.$year = $year;
|
|
302
317
|
this.$month = $month;
|
|
@@ -36,6 +36,12 @@ export class LocalTime {
|
|
|
36
36
|
}
|
|
37
37
|
return t;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Create LocalTime from unixTimestamp in milliseconds (not in seconds).
|
|
41
|
+
*/
|
|
42
|
+
static ofMillis(millis) {
|
|
43
|
+
return LocalTime.of(new Date(millis));
|
|
44
|
+
}
|
|
39
45
|
/**
|
|
40
46
|
* Returns null if invalid
|
|
41
47
|
*/
|
|
@@ -93,7 +99,7 @@ export class LocalTime {
|
|
|
93
99
|
return new LocalTime(new Date());
|
|
94
100
|
}
|
|
95
101
|
static fromComponents(c) {
|
|
96
|
-
return new LocalTime(new Date(c.year, c.month - 1, c.day, c.hour, c.minute, c.second));
|
|
102
|
+
return new LocalTime(new Date(c.year, c.month - 1, c.day || 1, c.hour || 0, c.minute || 0, c.second || 0));
|
|
97
103
|
}
|
|
98
104
|
get(unit) {
|
|
99
105
|
if (unit === 'year') {
|
|
@@ -173,15 +179,11 @@ export class LocalTime {
|
|
|
173
179
|
return v === undefined ? this.get('second') : this.set('second', v);
|
|
174
180
|
}
|
|
175
181
|
setComponents(c, mutate = false) {
|
|
182
|
+
var _a;
|
|
176
183
|
const d = mutate ? this.$date : new Date(this.$date);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (c.month) {
|
|
181
|
-
d.setMonth(c.month - 1);
|
|
182
|
-
}
|
|
183
|
-
if (c.day) {
|
|
184
|
-
d.setDate(c.day);
|
|
184
|
+
// Year, month and day set all-at-once, to avoid 30/31 (and 28/29) mishap
|
|
185
|
+
if (c.day || c.month !== undefined || c.year !== undefined) {
|
|
186
|
+
d.setFullYear((_a = c.year) !== null && _a !== void 0 ? _a : d.getFullYear(), c.month ? c.month - 1 : d.getMonth(), c.day || d.getDate());
|
|
185
187
|
}
|
|
186
188
|
if (c.hour !== undefined) {
|
|
187
189
|
d.setHours(c.hour);
|
|
@@ -199,6 +201,10 @@ export class LocalTime {
|
|
|
199
201
|
num *= 7;
|
|
200
202
|
unit = 'day';
|
|
201
203
|
}
|
|
204
|
+
if (unit === 'year' || unit === 'month') {
|
|
205
|
+
const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate);
|
|
206
|
+
return mutate ? this : LocalTime.of(d);
|
|
207
|
+
}
|
|
202
208
|
return this.set(unit, this.get(unit) + num, mutate);
|
|
203
209
|
}
|
|
204
210
|
subtract(num, unit, mutate = false) {
|
|
@@ -212,33 +218,14 @@ export class LocalTime {
|
|
|
212
218
|
const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000;
|
|
213
219
|
if (!secDiff)
|
|
214
220
|
return 0;
|
|
215
|
-
if (unit === 'year' || unit === 'month') {
|
|
216
|
-
const sign = secDiff > 0 ? 1 : -1;
|
|
217
|
-
// Put items in descending order: "big minus small"
|
|
218
|
-
const [big, small] = sign === 1 ? [this.$date, date2] : [date2, this.$date];
|
|
219
|
-
if (unit === 'year') {
|
|
220
|
-
let years = big.getFullYear() - small.getFullYear();
|
|
221
|
-
const big2 = new Date(big);
|
|
222
|
-
const small2 = new Date(small);
|
|
223
|
-
big2.setFullYear(1584);
|
|
224
|
-
small2.setFullYear(1584);
|
|
225
|
-
if (big2 < small2)
|
|
226
|
-
years--;
|
|
227
|
-
return years * sign || 0;
|
|
228
|
-
}
|
|
229
|
-
if (unit === 'month') {
|
|
230
|
-
let months = (big.getFullYear() - small.getFullYear()) * 12 + big.getMonth() - small.getMonth();
|
|
231
|
-
const big2 = new Date(big);
|
|
232
|
-
const small2 = new Date(small);
|
|
233
|
-
big2.setFullYear(1584, 0);
|
|
234
|
-
small2.setFullYear(1584, 0);
|
|
235
|
-
if (big2 < small2)
|
|
236
|
-
months--;
|
|
237
|
-
return months * sign || 0;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
221
|
let r;
|
|
241
|
-
if (unit === '
|
|
222
|
+
if (unit === 'year') {
|
|
223
|
+
r = differenceInMonths(this.getDate(), date2) / 12;
|
|
224
|
+
}
|
|
225
|
+
else if (unit === 'month') {
|
|
226
|
+
r = differenceInMonths(this.getDate(), date2);
|
|
227
|
+
}
|
|
228
|
+
else if (unit === 'day') {
|
|
242
229
|
r = secDiff / SECONDS_IN_DAY;
|
|
243
230
|
}
|
|
244
231
|
else if (unit === 'week') {
|
|
@@ -483,7 +470,7 @@ export class LocalTime {
|
|
|
483
470
|
].join('');
|
|
484
471
|
}
|
|
485
472
|
toString() {
|
|
486
|
-
return
|
|
473
|
+
return this.toISODateTime();
|
|
487
474
|
}
|
|
488
475
|
toJSON() {
|
|
489
476
|
return this.unix();
|
|
@@ -538,29 +525,6 @@ function getWeekYear(date) {
|
|
|
538
525
|
return year - 1;
|
|
539
526
|
}
|
|
540
527
|
}
|
|
541
|
-
// function setWeekYear(
|
|
542
|
-
// date: Date,
|
|
543
|
-
// year: number,
|
|
544
|
-
// ): Date {
|
|
545
|
-
// const diff = differenceInCalendarDays(date, startOfWeekYear(date))
|
|
546
|
-
// const fourthOfJanuary = new Date(0)
|
|
547
|
-
// fourthOfJanuary.setFullYear(year, 0, 4)
|
|
548
|
-
// fourthOfJanuary.setHours(0, 0, 0, 0)
|
|
549
|
-
// date = startOfWeekYear(fourthOfJanuary)
|
|
550
|
-
// date.setDate(date.getDate() + diff)
|
|
551
|
-
// return date
|
|
552
|
-
// }
|
|
553
|
-
// function differenceInCalendarDays(
|
|
554
|
-
// dateLeft: Date,
|
|
555
|
-
// dateRight: Date,
|
|
556
|
-
// ): number {
|
|
557
|
-
// return Math.round((startOfDay(dateLeft).getTime() - startOfDay(dateRight).getTime()) / MILLISECONDS_IN_DAY)
|
|
558
|
-
// }
|
|
559
|
-
// function startOfDay(date: Date, mutate = false): Date {
|
|
560
|
-
// const d = mutate ? date : new Date(date)
|
|
561
|
-
// d.setHours(0, 0, 0, 0)
|
|
562
|
-
// return d
|
|
563
|
-
// }
|
|
564
528
|
// based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
|
|
565
529
|
function startOfWeek(date, mutate = false) {
|
|
566
530
|
const d = mutate ? date : new Date(date);
|
|
@@ -578,3 +542,36 @@ function endOfWeek(date, mutate = false) {
|
|
|
578
542
|
d.setDate(d.getDate() + diff);
|
|
579
543
|
return d;
|
|
580
544
|
}
|
|
545
|
+
function addMonths(d, num, mutate = false) {
|
|
546
|
+
if (!mutate)
|
|
547
|
+
d = new Date(d);
|
|
548
|
+
let day = d.getDate();
|
|
549
|
+
let month = d.getMonth() + 1 + num;
|
|
550
|
+
if (day < 29) {
|
|
551
|
+
d.setMonth(month - 1);
|
|
552
|
+
return d;
|
|
553
|
+
}
|
|
554
|
+
let year = d.getFullYear();
|
|
555
|
+
while (month > 12) {
|
|
556
|
+
year++;
|
|
557
|
+
month -= 12;
|
|
558
|
+
}
|
|
559
|
+
while (month < 1) {
|
|
560
|
+
year--;
|
|
561
|
+
month += 12;
|
|
562
|
+
}
|
|
563
|
+
const monthLen = LocalDate.getMonthLength(year, month);
|
|
564
|
+
if (day > monthLen)
|
|
565
|
+
day = monthLen;
|
|
566
|
+
d.setFullYear(year, month - 1, day);
|
|
567
|
+
return d;
|
|
568
|
+
}
|
|
569
|
+
function differenceInMonths(a, b) {
|
|
570
|
+
if (a.getDate() < b.getDate())
|
|
571
|
+
return -differenceInMonths(b, a);
|
|
572
|
+
const wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth());
|
|
573
|
+
const anchor = addMonths(a, wholeMonthDiff).getTime();
|
|
574
|
+
const sign = b.getTime() - anchor >= 0 ? 1 : -1;
|
|
575
|
+
const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime();
|
|
576
|
+
return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign);
|
|
577
|
+
}
|
package/package.json
CHANGED
|
@@ -269,7 +269,15 @@ export class LocalDate {
|
|
|
269
269
|
if (unit === 'year') {
|
|
270
270
|
let years = big.$year - small.$year
|
|
271
271
|
|
|
272
|
-
if (
|
|
272
|
+
if (
|
|
273
|
+
big.$month < small.$month ||
|
|
274
|
+
(big.$month === small.$month &&
|
|
275
|
+
big.$day < small.$day &&
|
|
276
|
+
!(
|
|
277
|
+
big.$day === LocalDate.getMonthLength(big.$year, big.$month) &&
|
|
278
|
+
small.$day === LocalDate.getMonthLength(small.$year, small.$month)
|
|
279
|
+
))
|
|
280
|
+
) {
|
|
273
281
|
years--
|
|
274
282
|
}
|
|
275
283
|
|
|
@@ -278,7 +286,12 @@ export class LocalDate {
|
|
|
278
286
|
|
|
279
287
|
if (unit === 'month') {
|
|
280
288
|
let months = (big.$year - small.$year) * 12 + (big.$month - small.$month)
|
|
281
|
-
if (big.$day < small.$day)
|
|
289
|
+
if (big.$day < small.$day) {
|
|
290
|
+
const bigMonthLen = LocalDate.getMonthLength(big.$year, big.$month)
|
|
291
|
+
if (big.$day !== bigMonthLen || small.$day < bigMonthLen) {
|
|
292
|
+
months--
|
|
293
|
+
}
|
|
294
|
+
}
|
|
282
295
|
return months * sign || 0
|
|
283
296
|
}
|
|
284
297
|
|
|
@@ -324,21 +337,37 @@ export class LocalDate {
|
|
|
324
337
|
$year += num
|
|
325
338
|
}
|
|
326
339
|
|
|
340
|
+
// check month overflow
|
|
341
|
+
while ($month > 12) {
|
|
342
|
+
$year += 1
|
|
343
|
+
$month -= 12
|
|
344
|
+
}
|
|
345
|
+
while ($month < 1) {
|
|
346
|
+
$year -= 1
|
|
347
|
+
$month += 12
|
|
348
|
+
}
|
|
349
|
+
|
|
327
350
|
// check day overflow
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
351
|
+
// Applies not only for 'day' unit, but also e.g 2022-05-31 plus 1 month should be 2022-06-30 (not 31!)
|
|
352
|
+
if ($day < 1) {
|
|
353
|
+
while ($day < 1) {
|
|
354
|
+
$month -= 1
|
|
355
|
+
if ($month < 1) {
|
|
356
|
+
$year -= 1
|
|
357
|
+
$month += 12
|
|
358
|
+
}
|
|
336
359
|
|
|
337
|
-
|
|
360
|
+
$day += LocalDate.getMonthLength($year, $month)
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
let monLen = LocalDate.getMonthLength($year, $month)
|
|
364
|
+
|
|
365
|
+
if (unit !== 'day') {
|
|
366
|
+
if ($day > monLen) {
|
|
367
|
+
// Case of 2022-05-31 plus 1 month should be 2022-06-30, not 31
|
|
368
|
+
$day = monLen
|
|
338
369
|
}
|
|
339
370
|
} else {
|
|
340
|
-
let monLen = LocalDate.getMonthLength($year, $month)
|
|
341
|
-
|
|
342
371
|
while ($day > monLen) {
|
|
343
372
|
$day -= monLen
|
|
344
373
|
$month += 1
|
|
@@ -352,16 +381,6 @@ export class LocalDate {
|
|
|
352
381
|
}
|
|
353
382
|
}
|
|
354
383
|
|
|
355
|
-
// check month overflow
|
|
356
|
-
while ($month > 12) {
|
|
357
|
-
$year += 1
|
|
358
|
-
$month -= 12
|
|
359
|
-
}
|
|
360
|
-
while ($month < 1) {
|
|
361
|
-
$year -= 1
|
|
362
|
-
$month += 12
|
|
363
|
-
}
|
|
364
|
-
|
|
365
384
|
if (mutate) {
|
|
366
385
|
this.$year = $year
|
|
367
386
|
this.$month = $month
|
|
@@ -56,6 +56,13 @@ export class LocalTime {
|
|
|
56
56
|
return t
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Create LocalTime from unixTimestamp in milliseconds (not in seconds).
|
|
61
|
+
*/
|
|
62
|
+
static ofMillis(millis: number): LocalTime {
|
|
63
|
+
return LocalTime.of(new Date(millis))
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
/**
|
|
60
67
|
* Returns null if invalid
|
|
61
68
|
*/
|
|
@@ -123,7 +130,9 @@ export class LocalTime {
|
|
|
123
130
|
static fromComponents(
|
|
124
131
|
c: { year: number; month: number } & Partial<LocalTimeComponents>,
|
|
125
132
|
): LocalTime {
|
|
126
|
-
return new LocalTime(
|
|
133
|
+
return new LocalTime(
|
|
134
|
+
new Date(c.year, c.month - 1, c.day || 1, c.hour || 0, c.minute || 0, c.second || 0),
|
|
135
|
+
)
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
get(unit: LocalTimeUnit): number {
|
|
@@ -227,15 +236,15 @@ export class LocalTime {
|
|
|
227
236
|
setComponents(c: Partial<LocalTimeComponents>, mutate = false): LocalTime {
|
|
228
237
|
const d = mutate ? this.$date : new Date(this.$date)
|
|
229
238
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
d.setDate(c.day)
|
|
239
|
+
// Year, month and day set all-at-once, to avoid 30/31 (and 28/29) mishap
|
|
240
|
+
if (c.day || c.month !== undefined || c.year !== undefined) {
|
|
241
|
+
d.setFullYear(
|
|
242
|
+
c.year ?? d.getFullYear(),
|
|
243
|
+
c.month ? c.month - 1 : d.getMonth(),
|
|
244
|
+
c.day || d.getDate(),
|
|
245
|
+
)
|
|
238
246
|
}
|
|
247
|
+
|
|
239
248
|
if (c.hour !== undefined) {
|
|
240
249
|
d.setHours(c.hour)
|
|
241
250
|
}
|
|
@@ -254,6 +263,12 @@ export class LocalTime {
|
|
|
254
263
|
num *= 7
|
|
255
264
|
unit = 'day'
|
|
256
265
|
}
|
|
266
|
+
|
|
267
|
+
if (unit === 'year' || unit === 'month') {
|
|
268
|
+
const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate)
|
|
269
|
+
return mutate ? this : LocalTime.of(d)
|
|
270
|
+
}
|
|
271
|
+
|
|
257
272
|
return this.set(unit, this.get(unit) + num, mutate)
|
|
258
273
|
}
|
|
259
274
|
|
|
@@ -271,37 +286,13 @@ export class LocalTime {
|
|
|
271
286
|
const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
|
|
272
287
|
if (!secDiff) return 0
|
|
273
288
|
|
|
274
|
-
if (unit === 'year' || unit === 'month') {
|
|
275
|
-
const sign = secDiff > 0 ? 1 : -1
|
|
276
|
-
|
|
277
|
-
// Put items in descending order: "big minus small"
|
|
278
|
-
const [big, small] = sign === 1 ? [this.$date, date2] : [date2, this.$date]
|
|
279
|
-
|
|
280
|
-
if (unit === 'year') {
|
|
281
|
-
let years = big.getFullYear() - small.getFullYear()
|
|
282
|
-
const big2 = new Date(big)
|
|
283
|
-
const small2 = new Date(small)
|
|
284
|
-
big2.setFullYear(1584)
|
|
285
|
-
small2.setFullYear(1584)
|
|
286
|
-
if (big2 < small2) years--
|
|
287
|
-
return years * sign || 0
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (unit === 'month') {
|
|
291
|
-
let months =
|
|
292
|
-
(big.getFullYear() - small.getFullYear()) * 12 + big.getMonth() - small.getMonth()
|
|
293
|
-
const big2 = new Date(big)
|
|
294
|
-
const small2 = new Date(small)
|
|
295
|
-
big2.setFullYear(1584, 0)
|
|
296
|
-
small2.setFullYear(1584, 0)
|
|
297
|
-
if (big2 < small2) months--
|
|
298
|
-
return months * sign || 0
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
289
|
let r
|
|
303
290
|
|
|
304
|
-
if (unit === '
|
|
291
|
+
if (unit === 'year') {
|
|
292
|
+
r = differenceInMonths(this.getDate(), date2) / 12
|
|
293
|
+
} else if (unit === 'month') {
|
|
294
|
+
r = differenceInMonths(this.getDate(), date2)
|
|
295
|
+
} else if (unit === 'day') {
|
|
305
296
|
r = secDiff / SECONDS_IN_DAY
|
|
306
297
|
} else if (unit === 'week') {
|
|
307
298
|
r = secDiff / (7 * 24 * 60 * 60)
|
|
@@ -585,7 +576,7 @@ export class LocalTime {
|
|
|
585
576
|
}
|
|
586
577
|
|
|
587
578
|
toString(): string {
|
|
588
|
-
return
|
|
579
|
+
return this.toISODateTime()
|
|
589
580
|
}
|
|
590
581
|
|
|
591
582
|
toJSON(): UnixTimestampNumber {
|
|
@@ -649,32 +640,6 @@ function getWeekYear(date: Date): number {
|
|
|
649
640
|
}
|
|
650
641
|
}
|
|
651
642
|
|
|
652
|
-
// function setWeekYear(
|
|
653
|
-
// date: Date,
|
|
654
|
-
// year: number,
|
|
655
|
-
// ): Date {
|
|
656
|
-
// const diff = differenceInCalendarDays(date, startOfWeekYear(date))
|
|
657
|
-
// const fourthOfJanuary = new Date(0)
|
|
658
|
-
// fourthOfJanuary.setFullYear(year, 0, 4)
|
|
659
|
-
// fourthOfJanuary.setHours(0, 0, 0, 0)
|
|
660
|
-
// date = startOfWeekYear(fourthOfJanuary)
|
|
661
|
-
// date.setDate(date.getDate() + diff)
|
|
662
|
-
// return date
|
|
663
|
-
// }
|
|
664
|
-
|
|
665
|
-
// function differenceInCalendarDays(
|
|
666
|
-
// dateLeft: Date,
|
|
667
|
-
// dateRight: Date,
|
|
668
|
-
// ): number {
|
|
669
|
-
// return Math.round((startOfDay(dateLeft).getTime() - startOfDay(dateRight).getTime()) / MILLISECONDS_IN_DAY)
|
|
670
|
-
// }
|
|
671
|
-
|
|
672
|
-
// function startOfDay(date: Date, mutate = false): Date {
|
|
673
|
-
// const d = mutate ? date : new Date(date)
|
|
674
|
-
// d.setHours(0, 0, 0, 0)
|
|
675
|
-
// return d
|
|
676
|
-
// }
|
|
677
|
-
|
|
678
643
|
// based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
|
|
679
644
|
function startOfWeek(date: Date, mutate = false): Date {
|
|
680
645
|
const d = mutate ? date : new Date(date)
|
|
@@ -697,3 +662,41 @@ function endOfWeek(date: Date, mutate = false): Date {
|
|
|
697
662
|
d.setDate(d.getDate() + diff)
|
|
698
663
|
return d
|
|
699
664
|
}
|
|
665
|
+
|
|
666
|
+
function addMonths(d: Date, num: number, mutate = false): Date {
|
|
667
|
+
if (!mutate) d = new Date(d)
|
|
668
|
+
|
|
669
|
+
let day = d.getDate()
|
|
670
|
+
let month = d.getMonth() + 1 + num
|
|
671
|
+
|
|
672
|
+
if (day < 29) {
|
|
673
|
+
d.setMonth(month - 1)
|
|
674
|
+
return d
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
let year = d.getFullYear()
|
|
678
|
+
|
|
679
|
+
while (month > 12) {
|
|
680
|
+
year++
|
|
681
|
+
month -= 12
|
|
682
|
+
}
|
|
683
|
+
while (month < 1) {
|
|
684
|
+
year--
|
|
685
|
+
month += 12
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const monthLen = LocalDate.getMonthLength(year, month)
|
|
689
|
+
if (day > monthLen) day = monthLen
|
|
690
|
+
|
|
691
|
+
d.setFullYear(year, month - 1, day)
|
|
692
|
+
return d
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function differenceInMonths(a: Date, b: Date): number {
|
|
696
|
+
if (a.getDate() < b.getDate()) return -differenceInMonths(b, a)
|
|
697
|
+
const wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth())
|
|
698
|
+
const anchor = addMonths(a, wholeMonthDiff).getTime()
|
|
699
|
+
const sign = b.getTime() - anchor >= 0 ? 1 : -1
|
|
700
|
+
const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime()
|
|
701
|
+
return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign)
|
|
702
|
+
}
|