Repository: iamkun/dayjs (JavaScript)

Issue:
dayjs().add(Duration) API
Hello, I'm replacing moment.js with `dayjs` in a project.

I'm not sure if the APIs are not compatible at this point or I'm making a mistake here:

```
// the old code basically had `moment` where you see dayjs now

const date1 = dayjs('2018-04-04T16:00:00.000Z')
const date2 = dayjs();
const duration = dayjs.duration(dayjs().diff(date1));
const date3 = date2.add(duration);
console.log(date3);
```

the output of the above code is:
```
    d {
      '$L': 'en',
      '$u': undefined,
      '$d': Invalid Date,
      '$x': {},
      '$y': NaN,
      '$M': NaN,
      '$D': NaN,
      '$W': NaN,
      '$H': NaN,
      '$m': NaN,
      '$s': NaN,
      '$ms': NaN
    }
```
Looking at the implementation of `add`:

https://github.com/iamkun/dayjs/blob/f95ac15a4577ae5a3d1ce353872a2cd9fc454bc2/src/index.js#L220-L221

it always expects the first parameter to be a number so that could one reason why this isn't working.

In moment.js we could add a `Duration` to a Moment object: https://momentjs.com/docs/#/manipulating/add/

One workaround would be to convert the duration to milliseconds and add it to the `date2`:

```
const date1 = dayjs('2018-04-04T16:00:00.000Z')
const date2 = dayjs();
const duration = dayjs.duration(dayjs().diff(date1)).asMilliseconds();;
const date3 = date2.add(duration, 'ms');
```


Discussion:
Day.js does not support adding `Duration` at present.

However, we could support it in the Duration plugin if needed. 
This will be a nice feature added to the `Duration` plugin.

It's much like the workaround code mentioned above. 

Feel free to draft a PR if anyone is interested in it.


:tada: This issue has been resolved in version 1.9.2 :tada:

The release is available on:
- [npm package (@latest dist-tag)](https://www.npmjs.com/package/dayjs/v/1.9.2)
- [GitHub release](https://github.com/iamkun/dayjs/releases/tag/v1.9.2)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
Thanks for this feature !

There are issues with the calculation though:

```ts
dayjs('2020-01-01').add(1, 'month') // 2020-02-01 ✅
dayjs('2020-01-01').add(dayjs.duration(1, 'month')) // 2020-01-31 ❌
```

Reproduction example:
https://runkit.com/franky47/600431082af3c5001a19f25d

This is because the `add` method uses the duration in milliseconds, which has no notion of context (years, months, days and minutes have variable numbers of milliseconds in them).
@iamkun I am finding that in 1.11.9 this does not work, with the same invalid date output. It might be a regression!



Patch:
diff --git a/src/plugin/duration/index.js b/src/plugin/duration/index.js
index dad125f11..322f58a63 100644
--- a/src/plugin/duration/index.js
+++ b/src/plugin/duration/index.js
@@ -176,4 +176,13 @@ export default (option, Dayjs, dayjs) => {
     return wrapper(input, { $l }, unit)
   }
   dayjs.isDuration = isDuration
+
+  const oldAdd = Dayjs.prototype.add
+  Dayjs.prototype.add = function (addition, units) {
+    if (isDuration(addition)) {
+      addition = addition.asMilliseconds()
+      units = 'ms'
+    }
+    return oldAdd.bind(this)(addition, units)
+  }
 }


Test patch:
diff --git a/test/plugin/duration.test.js b/test/plugin/duration.test.js
index d153da43a..0b70bdeb4 100644
--- a/test/plugin/duration.test.js
+++ b/test/plugin/duration.test.js
@@ -138,6 +138,12 @@ describe('Add', () => {
   expect(a.add({ days: 5 }).days()).toBe(6)
 })
 
+describe('Add duration', () => {
+  const a = dayjs('2020-10-01')
+  const days = dayjs.duration(2, 'days')
+  expect(a.add(days).format('YYYY-MM-DD')).toBe('2020-10-03')
+})
+
 describe('Subtract', () => {
   const a = dayjs.duration(3, 'days')
   const b = dayjs.duration(2, 'days')