Repository: iamkun/dayjs

Problem statement:
dayjs init data seems not right
When I init data like this, not like `moment.js` can make right way to parse the Date String.

```
console.log(dayjs('2018-01'))

output:
Fri Dec 01 2017 00:00:00 GMT+0800 (CST
```

But I can solve by pass the Date, and init with `new Date(string)`.

```
console.log(dayjs(new Date('2018-01')))

output:
Mon Jan 01 2018 08:00:00 GMT+0800 (CST)
```

希望你能看得懂...


Hints:
It's a bug. However, maybe dayjs('2018-01-01') would be better? 

'2018-01' could either be '2018-01-01' or '2018-01-31'



Patch:
diff --git a/index.d.ts b/index.d.ts
--- a/index.d.ts
+++ b/index.d.ts
@@ -87,7 +87,7 @@ declare namespace dayjs {
 
   export type PluginFunc = (option: ConfigType, d1: Dayjs, d2: Dayjs) => void
 
-  export function extend(plugin: PluginFunc, option: ConfigType): Dayjs
+  export function extend(plugin: PluginFunc, option?: ConfigType): Dayjs
 
   export function local(arg1: any, arg2?: any): void
 }
diff --git a/src/constant.js b/src/constant.js
--- a/src/constant.js
+++ b/src/constant.js
@@ -24,7 +24,7 @@ export const DATE = 'date'
 export const FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ssZ'
 
 // regex
-export const REGEX_PARSE = /^(\d{4})-?(\d{0,2})-?(\d{0,2})(.*?(\d{1,2}):(\d{1,2}):(\d{1,2}))?.?(\d{1,3})?$/
+export const REGEX_PARSE = /^(\d{4})-?(\d{1,2})-?(\d{0,2})(.*?(\d{1,2}):(\d{1,2}):(\d{1,2}))?.?(\d{1,3})?$/
 export const REGEX_FORMAT = /\[.*?\]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g
 
 export const en = {
diff --git a/src/index.js b/src/index.js
--- a/src/index.js
+++ b/src/index.js
@@ -52,7 +52,7 @@ const parseDate = (date) => {
   if ((typeof date === 'string') && (reg = date.match(C.REGEX_PARSE))) {
     // 2018-08-08 or 20180808
     return new Date(
-      reg[1], (reg[2] - 1) || 0, reg[3] || 1,
+      reg[1], reg[2] - 1, reg[3] || 1,
       reg[5] || 0, reg[6] || 0, reg[7] || 0, reg[8] || 0
     )
   }
@@ -230,15 +230,15 @@ class Dayjs {
     number = Number(number) // eslint-disable-line no-param-reassign
     // units === 'ms' hard code here, will update in next release
     const unit = (units && (units.length === 1 || units === 'ms')) ? units : Utils.prettyUnit(units)
+    const instanceFactory = (u, n) => {
+      const date = this.set(C.DATE, 1).set(u, n + number)
+      return date.set(C.DATE, Math.min(this.$D, date.daysInMonth()))
+    }
     if (['M', C.M].indexOf(unit) > -1) {
-      let date = this.set(C.DATE, 1).set(C.M, this.$M + number)
-      date = date.set(C.DATE, Math.min(this.$D, date.daysInMonth()))
-      return date
+      return instanceFactory(C.M, this.$M)
     }
     if (['y', C.Y].indexOf(unit) > -1) {
-      let date = this.set(C.DATE, 1).set(C.Y, this.$y + number)
-      date = date.set(C.DATE, Math.min(this.$D, date.daysInMonth()))
-      return date
+      return instanceFactory(C.Y, this.$y)
     }
     let step
     switch (unit) {
diff --git a/src/utils.js b/src/utils.js
--- a/src/utils.js
+++ b/src/utils.js
@@ -15,16 +15,9 @@ const monthDiff = (a, b) => {
   // function from moment.js in order to keep the same result
   const wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month())
   const anchor = a.clone().add(wholeMonthDiff, 'months')
-  let anchor2
-  let adjust
-  if (b - anchor < 0) {
-    anchor2 = a.clone().add(wholeMonthDiff - 1, 'months')
-    adjust = (b - anchor) / (anchor - anchor2)
-  } else {
-    anchor2 = a.clone().add(wholeMonthDiff + 1, 'months')
-    adjust = (b - anchor) / (anchor2 - anchor)
-  }
-  return Number(-(wholeMonthDiff + adjust))
+  const c = b - anchor < 0
+  const anchor2 = a.clone().add(wholeMonthDiff + (c ? -1 : 1), 'months')
+  return Number(-(wholeMonthDiff + ((b - anchor) / (c ? (anchor - anchor2) : (anchor2 - anchor)))))
 }
 
 const absFloor = n => (n < 0 ? Math.ceil(n) || 0 : Math.floor(n))


Test patch:
diff --git a/test/manipulate.test.js b/test/manipulate.test.js
--- a/test/manipulate.test.js
+++ b/test/manipulate.test.js
@@ -43,6 +43,8 @@ it('Add Time days', () => {
   expect(dayjs().add(1, 'M').valueOf()).toBe(moment().add(1, 'M').valueOf())
   expect(dayjs().add(1, 'y').valueOf()).toBe(moment().add(1, 'y').valueOf())
   expect(dayjs('20111031').add(1, 'months').valueOf()).toBe(moment('20111031').add(1, 'months').valueOf())
+  expect(dayjs('20160131').add(1, 'months').valueOf()).toBe(moment('20160131').add(1, 'months').valueOf())
+  expect(dayjs('20160229').add(1, 'year').valueOf()).toBe(moment('20160229').add(1, 'year').valueOf())
 
   expect(dayjs().add('2', 'years').valueOf()).toBe(moment().add('2', 'years').valueOf())
 })
diff --git a/test/parse.test.js b/test/parse.test.js
--- a/test/parse.test.js
+++ b/test/parse.test.js
@@ -29,6 +29,10 @@ describe('Parse', () => {
     expect(dayjs(d).valueOf()).toBe(moment(d).valueOf()) // not recommend
     d = '2018-4-1 1:1:1:223'
     expect(dayjs(d).valueOf()).toBe(moment(d).valueOf()) // not recommend
+    d = '2018-01'
+    expect(dayjs(d).valueOf()).toBe(moment(d).valueOf()) // not recommend
+    d = '2018'
+    expect(dayjs(d).format()).toBe(moment(d).format()) // not recommend
   })
 
   it('String ISO 8601 date, time and zone', () => {