@naturalcycles/js-lib 14.99.3 → 14.99.4

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.
@@ -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 || (big.$month === small.$month && big.$day < small.$day)) {
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
- months--;
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
- if (unit === 'day') {
271
- if ($day < 1) {
272
- while ($day < 1) {
273
- $month -= 1;
274
- if ($month < 1) {
275
- $year -= 1;
276
- $month += 12;
277
- }
278
- $day += LocalDate.getMonthLength($year, $month);
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;
@@ -177,14 +177,9 @@ class LocalTime {
177
177
  }
178
178
  setComponents(c, mutate = false) {
179
179
  const d = mutate ? this.$date : new Date(this.$date);
180
- if (c.year) {
181
- d.setFullYear(c.year);
182
- }
183
- if (c.month) {
184
- d.setMonth(c.month - 1);
185
- }
186
- if (c.day) {
187
- d.setDate(c.day);
180
+ // Year, month and day set all-at-once, to avoid 30/31 (and 28/29) mishap
181
+ if (c.day || c.month !== undefined || c.year !== undefined) {
182
+ d.setFullYear(c.year ?? d.getFullYear(), c.month ? c.month - 1 : d.getMonth(), c.day || d.getDate());
188
183
  }
189
184
  if (c.hour !== undefined) {
190
185
  d.setHours(c.hour);
@@ -202,6 +197,10 @@ class LocalTime {
202
197
  num *= 7;
203
198
  unit = 'day';
204
199
  }
200
+ if (unit === 'year' || unit === 'month') {
201
+ const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate);
202
+ return mutate ? this : LocalTime.of(d);
203
+ }
205
204
  return this.set(unit, this.get(unit) + num, mutate);
206
205
  }
207
206
  subtract(num, unit, mutate = false) {
@@ -215,33 +214,14 @@ class LocalTime {
215
214
  const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000;
216
215
  if (!secDiff)
217
216
  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
217
  let r;
244
- if (unit === 'day') {
218
+ if (unit === 'year') {
219
+ r = differenceInMonths(this.getDate(), date2) / 12;
220
+ }
221
+ else if (unit === 'month') {
222
+ r = differenceInMonths(this.getDate(), date2);
223
+ }
224
+ else if (unit === 'day') {
245
225
  r = secDiff / SECONDS_IN_DAY;
246
226
  }
247
227
  else if (unit === 'week') {
@@ -486,7 +466,7 @@ class LocalTime {
486
466
  ].join('');
487
467
  }
488
468
  toString() {
489
- return String(this.unix());
469
+ return this.toISODateTime();
490
470
  }
491
471
  toJSON() {
492
472
  return this.unix();
@@ -543,29 +523,6 @@ function getWeekYear(date) {
543
523
  return year - 1;
544
524
  }
545
525
  }
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
526
  // based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
570
527
  function startOfWeek(date, mutate = false) {
571
528
  const d = mutate ? date : new Date(date);
@@ -583,3 +540,36 @@ function endOfWeek(date, mutate = false) {
583
540
  d.setDate(d.getDate() + diff);
584
541
  return d;
585
542
  }
543
+ function addMonths(d, num, mutate = false) {
544
+ if (!mutate)
545
+ d = new Date(d);
546
+ let day = d.getDate();
547
+ let month = d.getMonth() + 1 + num;
548
+ if (day < 29) {
549
+ d.setMonth(month - 1);
550
+ return d;
551
+ }
552
+ let year = d.getFullYear();
553
+ while (month > 12) {
554
+ year++;
555
+ month -= 12;
556
+ }
557
+ while (month < 1) {
558
+ year--;
559
+ month += 12;
560
+ }
561
+ const monthLen = localDate_1.LocalDate.getMonthLength(year, month);
562
+ if (day > monthLen)
563
+ day = monthLen;
564
+ d.setFullYear(year, month - 1, day);
565
+ return d;
566
+ }
567
+ function differenceInMonths(a, b) {
568
+ if (a.getDate() < b.getDate())
569
+ return -differenceInMonths(b, a);
570
+ const wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth());
571
+ const anchor = addMonths(a, wholeMonthDiff).getTime();
572
+ const sign = b.getTime() - anchor >= 0 ? 1 : -1;
573
+ const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime();
574
+ return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign);
575
+ }
@@ -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 || (big.$month === small.$month && big.$day < small.$day)) {
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
- months--;
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
- if (unit === 'day') {
268
- if ($day < 1) {
269
- while ($day < 1) {
270
- $month -= 1;
271
- if ($month < 1) {
272
- $year -= 1;
273
- $month += 12;
274
- }
275
- $day += LocalDate.getMonthLength($year, $month);
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;
@@ -173,15 +173,11 @@ export class LocalTime {
173
173
  return v === undefined ? this.get('second') : this.set('second', v);
174
174
  }
175
175
  setComponents(c, mutate = false) {
176
+ var _a;
176
177
  const d = mutate ? this.$date : new Date(this.$date);
177
- if (c.year) {
178
- d.setFullYear(c.year);
179
- }
180
- if (c.month) {
181
- d.setMonth(c.month - 1);
182
- }
183
- if (c.day) {
184
- d.setDate(c.day);
178
+ // Year, month and day set all-at-once, to avoid 30/31 (and 28/29) mishap
179
+ if (c.day || c.month !== undefined || c.year !== undefined) {
180
+ d.setFullYear((_a = c.year) !== null && _a !== void 0 ? _a : d.getFullYear(), c.month ? c.month - 1 : d.getMonth(), c.day || d.getDate());
185
181
  }
186
182
  if (c.hour !== undefined) {
187
183
  d.setHours(c.hour);
@@ -199,6 +195,10 @@ export class LocalTime {
199
195
  num *= 7;
200
196
  unit = 'day';
201
197
  }
198
+ if (unit === 'year' || unit === 'month') {
199
+ const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate);
200
+ return mutate ? this : LocalTime.of(d);
201
+ }
202
202
  return this.set(unit, this.get(unit) + num, mutate);
203
203
  }
204
204
  subtract(num, unit, mutate = false) {
@@ -212,33 +212,14 @@ export class LocalTime {
212
212
  const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000;
213
213
  if (!secDiff)
214
214
  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
215
  let r;
241
- if (unit === 'day') {
216
+ if (unit === 'year') {
217
+ r = differenceInMonths(this.getDate(), date2) / 12;
218
+ }
219
+ else if (unit === 'month') {
220
+ r = differenceInMonths(this.getDate(), date2);
221
+ }
222
+ else if (unit === 'day') {
242
223
  r = secDiff / SECONDS_IN_DAY;
243
224
  }
244
225
  else if (unit === 'week') {
@@ -483,7 +464,7 @@ export class LocalTime {
483
464
  ].join('');
484
465
  }
485
466
  toString() {
486
- return String(this.unix());
467
+ return this.toISODateTime();
487
468
  }
488
469
  toJSON() {
489
470
  return this.unix();
@@ -538,29 +519,6 @@ function getWeekYear(date) {
538
519
  return year - 1;
539
520
  }
540
521
  }
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
522
  // based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
565
523
  function startOfWeek(date, mutate = false) {
566
524
  const d = mutate ? date : new Date(date);
@@ -578,3 +536,36 @@ function endOfWeek(date, mutate = false) {
578
536
  d.setDate(d.getDate() + diff);
579
537
  return d;
580
538
  }
539
+ function addMonths(d, num, mutate = false) {
540
+ if (!mutate)
541
+ d = new Date(d);
542
+ let day = d.getDate();
543
+ let month = d.getMonth() + 1 + num;
544
+ if (day < 29) {
545
+ d.setMonth(month - 1);
546
+ return d;
547
+ }
548
+ let year = d.getFullYear();
549
+ while (month > 12) {
550
+ year++;
551
+ month -= 12;
552
+ }
553
+ while (month < 1) {
554
+ year--;
555
+ month += 12;
556
+ }
557
+ const monthLen = LocalDate.getMonthLength(year, month);
558
+ if (day > monthLen)
559
+ day = monthLen;
560
+ d.setFullYear(year, month - 1, day);
561
+ return d;
562
+ }
563
+ function differenceInMonths(a, b) {
564
+ if (a.getDate() < b.getDate())
565
+ return -differenceInMonths(b, a);
566
+ const wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth());
567
+ const anchor = addMonths(a, wholeMonthDiff).getTime();
568
+ const sign = b.getTime() - anchor >= 0 ? 1 : -1;
569
+ const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime();
570
+ return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign);
571
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.99.3",
3
+ "version": "14.99.4",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -269,7 +269,15 @@ export class LocalDate {
269
269
  if (unit === 'year') {
270
270
  let years = big.$year - small.$year
271
271
 
272
- if (big.$month < small.$month || (big.$month === small.$month && big.$day < small.$day)) {
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) months--
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
- if (unit === 'day') {
329
- if ($day < 1) {
330
- while ($day < 1) {
331
- $month -= 1
332
- if ($month < 1) {
333
- $year -= 1
334
- $month += 12
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
- $day += LocalDate.getMonthLength($year, $month)
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
@@ -229,15 +229,15 @@ export class LocalTime {
229
229
  setComponents(c: Partial<LocalTimeComponents>, mutate = false): LocalTime {
230
230
  const d = mutate ? this.$date : new Date(this.$date)
231
231
 
232
- if (c.year) {
233
- d.setFullYear(c.year)
234
- }
235
- if (c.month) {
236
- d.setMonth(c.month - 1)
237
- }
238
- if (c.day) {
239
- d.setDate(c.day)
232
+ // Year, month and day set all-at-once, to avoid 30/31 (and 28/29) mishap
233
+ if (c.day || c.month !== undefined || c.year !== undefined) {
234
+ d.setFullYear(
235
+ c.year ?? d.getFullYear(),
236
+ c.month ? c.month - 1 : d.getMonth(),
237
+ c.day || d.getDate(),
238
+ )
240
239
  }
240
+
241
241
  if (c.hour !== undefined) {
242
242
  d.setHours(c.hour)
243
243
  }
@@ -256,6 +256,12 @@ export class LocalTime {
256
256
  num *= 7
257
257
  unit = 'day'
258
258
  }
259
+
260
+ if (unit === 'year' || unit === 'month') {
261
+ const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate)
262
+ return mutate ? this : LocalTime.of(d)
263
+ }
264
+
259
265
  return this.set(unit, this.get(unit) + num, mutate)
260
266
  }
261
267
 
@@ -273,37 +279,13 @@ export class LocalTime {
273
279
  const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
274
280
  if (!secDiff) return 0
275
281
 
276
- if (unit === 'year' || unit === 'month') {
277
- const sign = secDiff > 0 ? 1 : -1
278
-
279
- // Put items in descending order: "big minus small"
280
- const [big, small] = sign === 1 ? [this.$date, date2] : [date2, this.$date]
281
-
282
- if (unit === 'year') {
283
- let years = big.getFullYear() - small.getFullYear()
284
- const big2 = new Date(big)
285
- const small2 = new Date(small)
286
- big2.setFullYear(1584)
287
- small2.setFullYear(1584)
288
- if (big2 < small2) years--
289
- return years * sign || 0
290
- }
291
-
292
- if (unit === 'month') {
293
- let months =
294
- (big.getFullYear() - small.getFullYear()) * 12 + big.getMonth() - small.getMonth()
295
- const big2 = new Date(big)
296
- const small2 = new Date(small)
297
- big2.setFullYear(1584, 0)
298
- small2.setFullYear(1584, 0)
299
- if (big2 < small2) months--
300
- return months * sign || 0
301
- }
302
- }
303
-
304
282
  let r
305
283
 
306
- if (unit === 'day') {
284
+ if (unit === 'year') {
285
+ r = differenceInMonths(this.getDate(), date2) / 12
286
+ } else if (unit === 'month') {
287
+ r = differenceInMonths(this.getDate(), date2)
288
+ } else if (unit === 'day') {
307
289
  r = secDiff / SECONDS_IN_DAY
308
290
  } else if (unit === 'week') {
309
291
  r = secDiff / (7 * 24 * 60 * 60)
@@ -587,7 +569,7 @@ export class LocalTime {
587
569
  }
588
570
 
589
571
  toString(): string {
590
- return String(this.unix())
572
+ return this.toISODateTime()
591
573
  }
592
574
 
593
575
  toJSON(): UnixTimestampNumber {
@@ -651,32 +633,6 @@ function getWeekYear(date: Date): number {
651
633
  }
652
634
  }
653
635
 
654
- // function setWeekYear(
655
- // date: Date,
656
- // year: number,
657
- // ): Date {
658
- // const diff = differenceInCalendarDays(date, startOfWeekYear(date))
659
- // const fourthOfJanuary = new Date(0)
660
- // fourthOfJanuary.setFullYear(year, 0, 4)
661
- // fourthOfJanuary.setHours(0, 0, 0, 0)
662
- // date = startOfWeekYear(fourthOfJanuary)
663
- // date.setDate(date.getDate() + diff)
664
- // return date
665
- // }
666
-
667
- // function differenceInCalendarDays(
668
- // dateLeft: Date,
669
- // dateRight: Date,
670
- // ): number {
671
- // return Math.round((startOfDay(dateLeft).getTime() - startOfDay(dateRight).getTime()) / MILLISECONDS_IN_DAY)
672
- // }
673
-
674
- // function startOfDay(date: Date, mutate = false): Date {
675
- // const d = mutate ? date : new Date(date)
676
- // d.setHours(0, 0, 0, 0)
677
- // return d
678
- // }
679
-
680
636
  // based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
681
637
  function startOfWeek(date: Date, mutate = false): Date {
682
638
  const d = mutate ? date : new Date(date)
@@ -699,3 +655,41 @@ function endOfWeek(date: Date, mutate = false): Date {
699
655
  d.setDate(d.getDate() + diff)
700
656
  return d
701
657
  }
658
+
659
+ function addMonths(d: Date, num: number, mutate = false): Date {
660
+ if (!mutate) d = new Date(d)
661
+
662
+ let day = d.getDate()
663
+ let month = d.getMonth() + 1 + num
664
+
665
+ if (day < 29) {
666
+ d.setMonth(month - 1)
667
+ return d
668
+ }
669
+
670
+ let year = d.getFullYear()
671
+
672
+ while (month > 12) {
673
+ year++
674
+ month -= 12
675
+ }
676
+ while (month < 1) {
677
+ year--
678
+ month += 12
679
+ }
680
+
681
+ const monthLen = LocalDate.getMonthLength(year, month)
682
+ if (day > monthLen) day = monthLen
683
+
684
+ d.setFullYear(year, month - 1, day)
685
+ return d
686
+ }
687
+
688
+ function differenceInMonths(a: Date, b: Date): number {
689
+ if (a.getDate() < b.getDate()) return -differenceInMonths(b, a)
690
+ const wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth())
691
+ const anchor = addMonths(a, wholeMonthDiff).getTime()
692
+ const sign = b.getTime() - anchor >= 0 ? 1 : -1
693
+ const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime()
694
+ return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign)
695
+ }