DEV Community

Discussion on: How to humanize duration accurately in JavaScript, including weeks, months and years

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt • Edited

A bug found.

Missed by exactly 1 DAY sometimes, when reconstructing Date from Duration. #2

It is never more than one day. Two days or more never.

  1) 3mo 3w 2d 17h 46min 40s +/- 50%
       duration is precise:
     Error: Duration might be miscalculated: 16 / 1,000
      at Context.<anonymous> (src/index.spec.ts:96:19)
      at processImmediate (internal/timers.js:461:21)

{
  since: 2020-07-31T00:16:04.889Z,
  calculated: 2020-08-01T00:16:04.889Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4mo 5d 9h 47min 47s 241ms',
  error: '-0.779%'
}
{
  since: 2020-07-31T03:53:48.904Z,
  calculated: 2020-08-01T03:53:48.904Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4mo 5d 6h 10min 3s 226ms',
  error: '-0.780%'
}
{
  since: 2020-08-31T11:48:27.478Z,
  calculated: 2020-09-01T11:48:27.478Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 4d 22h 15min 24s 652ms',
  error: '-1.03%'
}
{
  since: 2020-08-31T14:00:40.771Z,
  calculated: 2020-09-01T14:00:40.771Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 4d 20h 3min 11s 359ms',
  error: '-1.03%'
}
{
  since: 2020-08-30T19:49:20.734Z,
  calculated: 2020-08-31T19:49:20.734Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 5d 14h 14min 31s 396ms',
  error: '-1.02%'
}
{
  since: 2020-08-31T13:28:24.720Z,
  calculated: 2020-09-01T13:28:24.720Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 4d 20h 35min 27s 410ms',
  error: '-1.03%'
}
{
  since: 2020-08-31T16:33:11.278Z,
  calculated: 2020-09-01T16:33:11.278Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 4d 17h 30min 40s 852ms',
  error: '-1.03%'
}
{
  since: 2020-08-30T23:02:44.816Z,
  calculated: 2020-08-31T23:02:44.816Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 5d 11h 1min 7s 314ms',
  error: '-1.03%'
}
{
  since: 2020-07-31T02:46:42.981Z,
  calculated: 2020-08-01T02:46:42.981Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4mo 5d 7h 17min 9s 149ms',
  error: '-0.779%'
}
{
  since: 2020-08-31T12:04:22.210Z,
  calculated: 2020-09-01T12:04:22.210Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 4d 21h 59min 29s 920ms',
  error: '-1.03%'
}
{
  since: 2020-08-30T23:10:44.277Z,
  calculated: 2020-08-31T23:10:44.277Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 5d 10h 53min 7s 853ms',
  error: '-1.03%'
}
{
  since: 2020-08-30T19:39:19.523Z,
  calculated: 2020-08-31T19:39:19.523Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 5d 14h 24min 32s 607ms',
  error: '-1.02%'
}
{
  since: 2020-07-31T16:21:26.519Z,
  calculated: 2020-08-01T16:21:26.519Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4mo 4d 17h 42min 25s 611ms',
  error: '-0.783%'
}
{
  since: 2020-07-31T03:47:39.171Z,
  calculated: 2020-08-01T03:47:39.171Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4mo 5d 6h 16min 12s 959ms',
  error: '-0.780%'
}
{
  since: 2020-08-30T20:58:57.362Z,
  calculated: 2020-08-31T20:58:57.362Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 5d 13h 4min 54s 768ms',
  error: '-1.03%'
}
{
  since: 2020-08-30T17:44:38.030Z,
  calculated: 2020-08-31T17:44:38.030Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3mo 5d 16h 19min 14s 100ms',
  error: '-1.02%'
}
Enter fullscreen mode Exit fullscreen mode
  2) 3y 2mo 9h 46min 40s +/- 50%
       duration is precise:
     Error: Duration might be miscalculated: 13 / 1,000
      at Context.<anonymous> (src/index.spec.ts:96:19)
      at processImmediate (internal/timers.js:461:21)

{
  since: 2016-08-31T06:50:17.654Z,
  calculated: 2016-09-01T06:50:17.654Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4y 3mo 5d 3h 13min 34s 476ms',
  error: '-0.0642%'
}
{
  since: 2018-07-31T15:16:31.460Z,
  calculated: 2018-08-01T15:16:31.460Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+2y 4mo 4d 18h 47min 20s 670ms',
  error: '-0.116%'
}
{
  since: 2017-12-30T22:24:20.233Z,
  calculated: 2017-12-31T22:24:20.233Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+2y 11mo 5d 11h 39min 31s 897ms',
  error: '-0.0933%'
}
{
  since: 2016-05-31T07:23:40.774Z,
  calculated: 2016-06-01T07:23:40.774Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4y 6mo 5d 2h 40min 11s 356ms',
  error: '-0.0606%'
}
{
  since: 2018-05-30T18:57:02.261Z,
  calculated: 2018-05-31T18:57:02.261Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+2y 6mo 5d 15h 6min 49s 869ms',
  error: '-0.109%'
}
{
  since: 2017-03-30T21:29:20.570Z,
  calculated: 2017-03-31T21:29:20.570Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+3y 8mo 5d 12h 34min 31s 560ms',
  error: '-0.0743%'
}
{
  since: 2018-01-30T17:27:28.142Z,
  calculated: 2018-01-31T17:27:28.142Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+2y 10mo 5d 16h 36min 23s 988ms',
  error: '-0.0961%'
}
{
  since: 2018-10-31T10:29:51.993Z,
  calculated: 2018-11-01T10:29:51.993Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+2y 1mo 4d 23h 34min 137ms',
  error: '-0.130%'
}
{
  since: 2016-05-31T12:21:50.150Z,
  calculated: 2016-06-01T12:21:50.150Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4y 6mo 4d 21h 42min 1s 980ms',
  error: '-0.0606%'
}
{
  since: 2016-05-31T15:36:26.792Z,
  calculated: 2016-06-01T15:36:26.792Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4y 6mo 4d 18h 27min 25s 338ms',
  error: '-0.0606%'
}
{
  since: 2016-08-31T00:41:19.869Z,
  calculated: 2016-09-01T00:41:19.869Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+4y 3mo 5d 9h 22min 32s 261ms',
  error: '-0.0642%'
}
{
  since: 2018-08-31T00:34:29.634Z,
  calculated: 2018-09-01T00:34:29.634Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+2y 3mo 5d 9h 29min 22s 496ms',
  error: '-0.121%'
}
{
  since: 2018-07-31T10:52:43.916Z,
  calculated: 2018-08-01T10:52:43.916Z,
  difference: '+1d',
  now: 2020-12-06T10:03:52.130Z,
  duration: '+2y 4mo 4d 23h 11min 8s 214ms',
  error: '-0.116%'
}
Enter fullscreen mode Exit fullscreen mode
  3) 31y 8mo 6d 1h 46min 40s +/- 50%
       duration is precise:
     Error: Duration might be miscalculated: 8 / 1,000
      at Context.<anonymous> (src/index.spec.ts:96:19)
      at processImmediate (internal/timers.js:461:21)

{
  since: 1991-01-31T09:27:18.628Z,
  calculated: 1991-02-01T09:27:18.628Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+29y 10mo 5d 41min 10s 18ms',
  error: '-0.00917%'
}
{
  since: 1999-07-31T04:30:48.634Z,
  calculated: 1999-08-01T04:30:48.634Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+21y 4mo 5d 5h 37min 40s 12ms',
  error: '-0.0128%'
}
{
  since: 1997-01-31T11:10:17.988Z,
  calculated: 1997-02-01T11:10:17.988Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+23y 10mo 4d 22h 58min 10s 658ms',
  error: '-0.0115%'
}
{
  since: 1988-10-31T02:42:42.649Z,
  calculated: 1988-11-01T02:42:42.649Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+32y 1mo 5d 7h 25min 45s 997ms',
  error: '-0.00853%'
}
{
  since: 2002-12-30T23:47:01.751Z,
  calculated: 2002-12-31T23:47:01.751Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+17y 11mo 5d 10h 21min 26s 895ms',
  error: '-0.0153%'
}
{
  since: 2004-10-31T15:13:41.103Z,
  calculated: 2004-11-01T15:13:41.103Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+16y 1mo 4d 18h 54min 47s 543ms',
  error: '-0.0170%'
}
{
  since: 1977-12-31T14:32:49.698Z,
  calculated: 1978-01-01T14:32:49.698Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+42y 11mo 4d 19h 35min 38s 948ms',
  error: '-0.00638%'
}
{
  since: 1976-10-30T22:43:19.120Z,
  calculated: 1976-10-31T22:43:19.120Z,
  difference: '+1d',
  now: 2020-12-06T10:08:28.646Z,
  duration: '+44y 1mo 5d 11h 25min 9s 526ms',
  error: '-0.00621%'
}
Enter fullscreen mode Exit fullscreen mode

The problem is, what is the real cause, why error is never more than 1 DAY, nor in other units?

Qualm is this code.

     this.d = this.parse((d) => d.getDate(), {
      get: (d) => d.getMonth(),
      set: (d, v) => d.setMonth(v),
      inc: (d) => {
        const y = d.getFullYear();
        let isLeapYear = true;
        if (y % 4) {
          isLeapYear = false;
        } else if (y % 100) {
          isLeapYear = true;
        } else if (y % 400) {
          isLeapYear = false;
        }

        return [
          31, // Jan
          isLeapYear ? 29 : 28, // Feb
          31, // Mar
          30, // Apr
          31, // May
          30, // Jun
          31, // Jul
          31, // Aug
          30, // Sep
          31, // Oct
          30, // Nov
          31, // Dec
        ][d.getMonth()];
      },
    });

...

  /**
   * @internal
   */
  private parse(
    current: (d: Date) => number,
    upper?: {
      get: (d: Date) => number;
      set: (d: Date, v: number) => void;
      inc: (d: Date) => number;
    }
  ) {
    let a = current(this.dates[1]) - current(this.dates[0]);

    if (upper) {
      while (a < 0) {
        upper.set(this.dates[1], upper.get(this.dates[1]) - 1);
        this.dates[1] = new Date(this.dates[1]);

        a += upper.inc(this.dates[1]);
      }
    }

    return a;
  }
Enter fullscreen mode Exit fullscreen mode

So, I reposted on Hashnode, starting with "bug"