Repository: iamkun/dayjs
Pull request #404 (2018-11-06 19:06:57)

Add plugin advancedCompare
We switched from moment.js to dayjs on a project that made heavy use of moment.js, especially comparisons considering units as well. Thought I'll share this a plugin. All tests taken from moment.js and adjusted to run with the dayjs test suite. 

codecov-io: # [Codecov](https://codecov.io/gh/iamkun/dayjs/pull/404?src=pr&el=h1) Report
> Merging [#404](https://codecov.io/gh/iamkun/dayjs/pull/404?src=pr&el=desc) into [master](https://codecov.io/gh/iamkun/dayjs/commit/8156a5c9d9cff3555c0d27857ddb4d613b5aa551?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/iamkun/dayjs/pull/404/graphs/tree.svg?width=650&token=3SZslYi4Y3&height=150&src=pr)](https://codecov.io/gh/iamkun/dayjs/pull/404?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #404   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          44     45    +1     
  Lines         428    446   +18     
  Branches       55     64    +9     
=====================================
+ Hits          428    446   +18
```


| [Impacted Files](https://codecov.io/gh/iamkun/dayjs/pull/404?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/plugin/advancedCompare/index.js](https://codecov.io/gh/iamkun/dayjs/pull/404/diff?src=pr&el=tree#diff-c3JjL3BsdWdpbi9hZHZhbmNlZENvbXBhcmUvaW5kZXguanM=) | `100% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/iamkun/dayjs/pull/404?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/iamkun/dayjs/pull/404?src=pr&el=footer). Last update [8156a5c...4bd47f3](https://codecov.io/gh/iamkun/dayjs/pull/404?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Liooo: such a hot topic for me, is this going to be merged and released soon?
suspectpart: @xxyuk @iamkun Can we merge this soon? It just got outdated with master (again). What is the status on your side?
suspectpart: > Thanks. There's a little problem with our npm account. And I'll merge this into next release after I get the account fixed.

Alright, thanks for the update. Can you make any projection on how long that might take? Is it days, weeks or months? 
iamkun: Not sure, but as soon as possible.

Patch:
diff --git a/index.d.ts b/index.d.ts
index 94854e4..a9bbf33 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -8,7 +8,7 @@ declare namespace dayjs {
   export type OptionType = { locale: string }
 
   export type UnitType = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year' | 'date'
-  
+
   interface DayjsObject {
     years: number
     months: number
@@ -18,68 +18,68 @@ declare namespace dayjs {
     seconds: number
     milliseconds: number
   }
-  
+
   class Dayjs {
     constructor (config?: ConfigType)
-  
+
     clone(): Dayjs
-  
+
     isValid(): boolean
-  
+
     year(): number
-  
+
     month(): number
-  
+
     date(): number
-  
+
     day(): number
-  
+
     hour(): number
-  
+
     minute(): number
-  
+
     second(): number
-  
+
     millisecond(): number
-  
+
     set(unit: UnitType, value: number): Dayjs
-  
+
     add(value: number, unit: UnitType): Dayjs
-  
+
     subtract(value: number, unit: UnitType): Dayjs
-  
+
     startOf(unit: UnitType): Dayjs
-  
+
     endOf(unit: UnitType): Dayjs
-  
+
     format(template?: string): string
-  
+
     diff(dayjs: Dayjs, unit: UnitType, float?: boolean): number
-  
+
     valueOf(): number
-  
+
     unix(): number
-  
+
     daysInMonth(): number
-  
+
     toDate(): Date
-  
+
     toArray(): number[]
-  
+
     toJSON(): string
-  
+
     toISOString(): string
-  
+
     toObject(): DayjsObject
-  
+
     toString(): string
-  
-    isBefore(dayjs: Dayjs): boolean
-  
-    isSame(dayjs: Dayjs): boolean
-  
-    isAfter(dayjs: Dayjs): boolean
-  
+
+    isBefore(dayjs: Dayjs, unit?: UnitType): boolean
+
+    isSame(dayjs: Dayjs, unit?: UnitType): boolean
+
+    isAfter(dayjs: Dayjs, unit?: UnitType): boolean
+
     isLeapYear(): boolean
 
     locale(arg1: any, arg2?: any): Dayjs
diff --git a/src/index.js b/src/index.js
index bc036c4..c45ef74 100644
--- a/src/index.js
+++ b/src/index.js
@@ -92,20 +92,17 @@ class Dayjs {
     return !(this.$d.toString() === 'Invalid Date')
   }
 
-  $compare(that) {
-    return this.valueOf() - dayjs(that).valueOf()
+  isSame(that, units) {
+    const other = dayjs(that)
+    return this.startOf(units) <= other && other <= this.endOf(units)
   }
 
-  isSame(that) {
-    return this.$compare(that) === 0
+  isAfter(that, units) {
+    return dayjs(that) < this.startOf(units)
   }
 
-  isBefore(that) {
-    return this.$compare(that) < 0
-  }
-
-  isAfter(that) {
-    return this.$compare(that) > 0
+  isBefore(that, units) {
+    return this.endOf(units) < dayjs(that)
   }
 
   year() {
diff --git a/src/plugin/isBetween/index.js b/src/plugin/isBetween/index.js
index a639fe5..69f81f2 100644
--- a/src/plugin/isBetween/index.js
+++ b/src/plugin/isBetween/index.js
@@ -1,10 +1,9 @@
 export default (o, c, d) => {
-  const proto = c.prototype
-  proto.isBetween = function (a, b) {
+  c.prototype.isBetween = function (a, b, u) {
     const dA = d(a)
     const dB = d(b)
 
-    return (this.isAfter(dA) && this.isBefore(dB)) || (this.isBefore(dA) && this.isAfter(dB))
+    return (this.isAfter(dA, u) && this.isBefore(dB, u))
+        || (this.isBefore(dA, u) && this.isAfter(dB, u))
   }
 }
-
diff --git a/src/plugin/isSameOrAfter/index.js b/src/plugin/isSameOrAfter/index.js
new file mode 100644
index 0000000..8a0cead
--- /dev/null
+++ b/src/plugin/isSameOrAfter/index.js
@@ -0,0 +1,5 @@
+export default (o, c) => {
+  c.prototype.isSameOrAfter = function (that, units) {
+    return this.isSame(that, units) || this.isAfter(that, units)
+  }
+}
diff --git a/src/plugin/isSameOrBefore/index.js b/src/plugin/isSameOrBefore/index.js
new file mode 100644
index 0000000..4ae5a40
--- /dev/null
+++ b/src/plugin/isSameOrBefore/index.js
@@ -0,0 +1,5 @@
+export default (o, c) => {
+  c.prototype.isSameOrBefore = function (that, units) {
+    return this.isSame(that, units) || this.isBefore(that, units)
+  }
+}


Test patch:
diff --git a/test/comparison.test.js b/test/comparison.test.js
new file mode 100644
index 0000000..f954382
--- /dev/null
+++ b/test/comparison.test.js
@@ -0,0 +1,530 @@
+import MockDate from 'mockdate'
+import dayjs from '../src'
+
+beforeEach(() => {
+  MockDate.set(new Date())
+})
+
+afterEach(() => {
+  MockDate.reset()
+})
+
+test('is same without units', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+
+  expect(m.isSame(dayjs(new Date(2012, 3, 2, 3, 5, 5, 10)))).toBe(false, 'year is later')
+  expect(m.isSame(dayjs(new Date(2010, 3, 2, 3, 3, 5, 10)))).toBe(false, 'year is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)))).toBe(false, 'month is later')
+  expect(m.isSame(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)))).toBe(false, 'month is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)))).toBe(false, 'day is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 1, 3, 4, 5, 10)))).toBe(false, 'day is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)))).toBe(false, 'hour is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 2, 4, 5, 10)))).toBe(false, 'hour is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)))).toBe(false, 'minute is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)))).toBe(false, 'minute is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)))).toBe(false, 'second is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 4, 11)))).toBe(false, 'second is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)))).toBe(true, 'millisecond match')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 5, 11)))).toBe(false, 'millisecond is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 5, 9)))).toBe(false, 'millisecond is earlier')
+  expect(m.isSame(m)).toBe(true, 'moments are the same as themselves')
+  expect(+m).toEqual(+mCopy, 'isSame second should not change moment')
+})
+
+test('is same year', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSame(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'year')).toBe(true, 'year match')
+  expect(m.isSame(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'years')).toBe(true, 'plural should work')
+  expect(m.isSame(dayjs(new Date(2012, 5, 6, 7, 8, 9, 10)), 'year')).toBe(false, 'year mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 0, 1, 0, 0, 0, 0)), 'year')).toBe(true, 'exact start of year')
+  expect(m.isSame(dayjs(new Date(2011, 11, 31, 23, 59, 59, 999)), 'year')).toBe(true, 'exact end of year')
+  expect(m.isSame(dayjs(new Date(2012, 0, 1, 0, 0, 0, 0)), 'year')).toBe(false, 'start of next year')
+  expect(m.isSame(dayjs(new Date(2010, 11, 31, 23, 59, 59, 999)), 'year')).toBe(false, 'end of previous year')
+  expect(m.isSame(m, 'year')).toBe(true, 'same moments are in the same year')
+  expect(+m).toEqual(+mCopy, 'isSame year should not change moment')
+})
+
+test('is same month', () => {
+  const m = dayjs(new Date(2011, 2, 3, 4, 5, 6, 7))
+  const mCopy = dayjs(m)
+  expect(m.isSame(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'month match')
+  expect(m.isSame(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'months')).toBe(true, 'plural should work')
+  expect(m.isSame(dayjs(new Date(2012, 2, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'year mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'month mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 2, 1, 0, 0, 0, 0)), 'month')).toBe(true, 'exact start of month')
+  expect(m.isSame(dayjs(new Date(2011, 2, 31, 23, 59, 59, 999)), 'month')).toBe(true, 'exact end of month')
+  expect(m.isSame(dayjs(new Date(2011, 3, 1, 0, 0, 0, 0)), 'month')).toBe(false, 'start of next month')
+  expect(m.isSame(dayjs(new Date(2011, 1, 27, 23, 59, 59, 999)), 'month')).toBe(false, 'end of previous month')
+  expect(m.isSame(m, 'month')).toBe(true, 'same moments are in the same month')
+  expect(+m).toEqual(+mCopy, 'isSame month should not change moment')
+})
+
+test('is same day', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'day match')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'days')).toBe(true, 'plural should work')
+  expect(m.isSame(dayjs(new Date(2012, 1, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'year mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 2, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'month mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 3, 7, 8, 9, 10)), 'day')).toBe(false, 'day mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 0, 0, 0, 0)), 'day')).toBe(true, 'exact start of day')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 23, 59, 59, 999)), 'day')).toBe(true, 'exact end of day')
+  expect(m.isSame(dayjs(new Date(2011, 1, 3, 0, 0, 0, 0)), 'day')).toBe(false, 'start of next day')
+  expect(m.isSame(dayjs(new Date(2011, 1, 1, 23, 59, 59, 999)), 'day')).toBe(false, 'end of previous day')
+  expect(m.isSame(m, 'day')).toBe(true, 'same moments are in the same day')
+  expect(+m).toEqual(+mCopy, 'isSame day should not change moment')
+})
+
+test('is same hour', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'hour match')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hours')).toBe(true, 'plural should work')
+  expect(m.isSame(dayjs(new Date(2012, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'year mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 2, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'month mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 3, 3, 8, 9, 10)), 'hour')).toBe(false, 'day mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 4, 8, 9, 10)), 'hour')).toBe(false, 'hour mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 0, 0, 0)), 'hour')).toBe(true, 'exact start of hour')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 59, 59, 999)), 'hour')).toBe(true, 'exact end of hour')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 4, 0, 0, 0)), 'hour')).toBe(false, 'start of next hour')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 2, 59, 59, 999)), 'hour')).toBe(false, 'end of previous hour')
+  expect(m.isSame(m, 'hour')).toBe(true, 'same moments are in the same hour')
+  expect(+m).toEqual(+mCopy, 'isSame hour should not change moment')
+})
+
+test('is same minute', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'minute match')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)), 'minutes')).toBe(true, 'plural should work')
+  expect(m.isSame(dayjs(new Date(2012, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'year mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 2, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'month mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 3, 3, 4, 9, 10)), 'minute')).toBe(false, 'day mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 4, 4, 9, 10)), 'minute')).toBe(false, 'hour mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 5, 9, 10)), 'minute')).toBe(false, 'minute mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 0, 0)), 'minute')).toBe(true, 'exact start of minute')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 59, 999)), 'minute')).toBe(true, 'exact end of minute')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 5, 0, 0)), 'minute')).toBe(false, 'start of next minute')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 3, 59, 999)), 'minute')).toBe(false, 'end of previous minute')
+  expect(m.isSame(m, 'minute')).toBe(true, 'same moments are in the same minute')
+  expect(+m).toEqual(+mCopy, 'isSame minute should not change moment')
+})
+
+test('is same second', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'second match')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)), 'seconds')).toBe(true, 'plural should work')
+  expect(m.isSame(dayjs(new Date(2012, 1, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'year mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'month mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 3, 3, 4, 5, 10)), 'second')).toBe(false, 'day mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 4, 4, 5, 10)), 'second')).toBe(false, 'hour mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 5, 5, 10)), 'second')).toBe(false, 'minute mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 6, 10)), 'second')).toBe(false, 'second mismatch')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 5, 0)), 'second')).toBe(true, 'exact start of second')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 5, 999)), 'second')).toBe(true, 'exact end of second')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 6, 0)), 'second')).toBe(false, 'start of next second')
+  expect(m.isSame(dayjs(new Date(2011, 1, 2, 3, 4, 4, 999)), 'second')).toBe(false, 'end of previous second')
+  expect(m.isSame(m, 'second')).toBe(true, 'same moments are in the same second')
+  expect(+m).toEqual(+mCopy, 'isSame second should not change moment')
+})
+
+test('is same millisecond', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'millisecond match')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'milliseconds')).toBe(true, 'plural should work')
+  expect(m.isSame(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'year is later')
+  expect(m.isSame(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'year is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'month is later')
+  expect(m.isSame(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'month is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'day is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 1, 1, 4, 5, 10)), 'millisecond')).toBe(false, 'day is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)), 'millisecond')).toBe(false, 'hour is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 1, 4, 1, 5, 10)), 'millisecond')).toBe(false, 'hour is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)), 'millisecond')).toBe(false, 'minute is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)), 'millisecond')).toBe(false, 'minute is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)), 'millisecond')).toBe(false, 'second is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 4, 5)), 'millisecond')).toBe(false, 'second is earlier')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 6, 11)), 'millisecond')).toBe(false, 'millisecond is later')
+  expect(m.isSame(dayjs(new Date(2011, 3, 2, 3, 4, 4, 9)), 'millisecond')).toBe(false, 'millisecond is earlier')
+  expect(m.isSame(m, 'millisecond')).toBe(true, 'same moments are in the same millisecond')
+  expect(+m).toEqual(+mCopy, 'isSame millisecond should not change moment')
+})
+
+test('is same with invalid moments', () => {
+  expect(dayjs(null).isSame(dayjs('2018-01-01'))).toBe(false, 'invalid moments are not considered equal')
+  expect(dayjs('2018-01-01').isSame(dayjs(null))).toBe(false, 'invalid moments are not considered equal')
+})
+
+
+// isAfter()
+
+test('is after year', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'year')).toBe(false, 'year match')
+  expect(m.isAfter(dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)), 'years')).toBe(true, 'plural should work')
+  expect(m.isAfter(dayjs(new Date(2013, 5, 6, 7, 8, 9, 10)), 'year')).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)), 'year')).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 0, 1, 0, 0, 0, 0)), 'year')).toBe(false, 'exact start of year')
+  expect(m.isAfter(dayjs(new Date(2011, 11, 31, 23, 59, 59, 999)), 'year')).toBe(false, 'exact end of year')
+  expect(m.isAfter(dayjs(new Date(2012, 0, 1, 0, 0, 0, 0)), 'year')).toBe(false, 'start of next year')
+  expect(m.isAfter(dayjs(new Date(2010, 11, 31, 23, 59, 59, 999)), 'year')).toBe(true, 'end of previous year')
+  expect(m.isAfter(dayjs(new Date(1980, 11, 31, 23, 59, 59, 999)), 'year')).toBe(true, 'end of year far before')
+  expect(m.isAfter(m, 'year')).toBe(false, 'same moments are not after the same year')
+  expect(+m).toEqual(+mCopy, 'isAfter year should not change moment')
+})
+
+test('is after month', () => {
+  const m = dayjs(new Date(2011, 2, 3, 4, 5, 6, 7))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'month match')
+  expect(m.isAfter(dayjs(new Date(2010, 2, 6, 7, 8, 9, 10)), 'months')).toBe(true, 'plural should work')
+  expect(m.isAfter(dayjs(new Date(2012, 2, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 2, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'month is later')
+  expect(m.isAfter(dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'month is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 2, 1, 0, 0, 0, 0)), 'month')).toBe(false, 'exact start of month')
+  expect(m.isAfter(dayjs(new Date(2011, 2, 31, 23, 59, 59, 999)), 'month')).toBe(false, 'exact end of month')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 0, 0, 0, 0)), 'month')).toBe(false, 'start of next month')
+  expect(m.isAfter(dayjs(new Date(2011, 1, 27, 23, 59, 59, 999)), 'month')).toBe(true, 'end of previous month')
+  expect(m.isAfter(dayjs(new Date(2010, 12, 31, 23, 59, 59, 999)), 'month')).toBe(true, 'later month but earlier year')
+  expect(m.isAfter(m, 'month')).toBe(false, 'same moments are not after the same month')
+  expect(+m).toEqual(+mCopy, 'isAfter month should not change moment')
+})
+
+test('is after day', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'day match')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 7, 8, 9, 10)), 'days')).toBe(true, 'plural should work')
+  expect(m.isAfter(dayjs(new Date(2012, 3, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 4, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'month is later')
+  expect(m.isAfter(dayjs(new Date(2011, 2, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'month is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 3, 7, 8, 9, 10)), 'day')).toBe(false, 'day is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 7, 8, 9, 10)), 'day')).toBe(true, 'day is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 0, 0, 0, 0)), 'day')).toBe(false, 'exact start of day')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 23, 59, 59, 999)), 'day')).toBe(false, 'exact end of day')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 3, 0, 0, 0, 0)), 'day')).toBe(false, 'start of next day')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 23, 59, 59, 999)), 'day')).toBe(true, 'end of previous day')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 10, 0, 0, 0, 0)), 'day')).toBe(true, 'later day but earlier year')
+  expect(m.isAfter(m, 'day')).toBe(false, 'same moments are not after the same day')
+  expect(+m).toEqual(+mCopy, 'isAfter day should not change moment')
+})
+
+test('is after hour', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'hour match')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 8, 9, 10)), 'hours')).toBe(true, 'plural should work')
+  expect(m.isAfter(dayjs(new Date(2012, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 4, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'month is later')
+  expect(m.isAfter(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'month is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 3, 3, 8, 9, 10)), 'hour')).toBe(false, 'day is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 3, 8, 9, 10)), 'hour')).toBe(true, 'day is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 4, 8, 9, 10)), 'hour')).toBe(false, 'hour is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'hour is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 0, 0, 0)), 'hour')).toBe(false, 'exact start of hour')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 59, 59, 999)), 'hour')).toBe(false, 'exact end of hour')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 4, 0, 0, 0)), 'hour')).toBe(false, 'start of next hour')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 2, 59, 59, 999)), 'hour')).toBe(true, 'end of previous hour')
+  expect(m.isAfter(m, 'hour')).toBe(false, 'same moments are not after the same hour')
+  expect(+m).toEqual(+mCopy, 'isAfter hour should not change moment')
+})
+
+test('is after minute', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'minute match')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 4, 9, 10)), 'minutes')).toBe(true, 'plural should work')
+  expect(m.isAfter(dayjs(new Date(2012, 3, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 4, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'month is later')
+  expect(m.isAfter(dayjs(new Date(2011, 2, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'month is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 3, 3, 4, 9, 10)), 'minute')).toBe(false, 'day is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 3, 4, 9, 10)), 'minute')).toBe(true, 'day is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 4, 4, 9, 10)), 'minute')).toBe(false, 'hour is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 2, 4, 9, 10)), 'minute')).toBe(true, 'hour is earler')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 5, 9, 10)), 'minute')).toBe(false, 'minute is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 3, 9, 10)), 'minute')).toBe(true, 'minute is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 0, 0)), 'minute')).toBe(false, 'exact start of minute')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 59, 999)), 'minute')).toBe(false, 'exact end of minute')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 5, 0, 0)), 'minute')).toBe(false, 'start of next minute')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 3, 59, 999)), 'minute')).toBe(true, 'end of previous minute')
+  expect(m.isAfter(m, 'minute')).toBe(false, 'same moments are not after the same minute')
+  expect(+m).toEqual(+mCopy, 'isAfter minute should not change moment')
+})
+
+test('is after second', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'second match')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'seconds')).toBe(true, 'plural should work')
+  expect(m.isAfter(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'month is later')
+  expect(m.isAfter(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'month is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)), 'second')).toBe(false, 'day is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 1, 4, 5, 10)), 'second')).toBe(true, 'day is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)), 'second')).toBe(false, 'hour is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 4, 1, 5, 10)), 'second')).toBe(true, 'hour is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)), 'second')).toBe(false, 'minute is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)), 'second')).toBe(true, 'minute is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)), 'second')).toBe(false, 'second is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 5)), 'second')).toBe(true, 'second is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 0)), 'second')).toBe(false, 'exact start of second')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 999)), 'second')).toBe(false, 'exact end of second')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 0)), 'second')).toBe(false, 'start of next second')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 999)), 'second')).toBe(true, 'end of previous second')
+  expect(m.isAfter(m, 'second')).toBe(false, 'same moments are not after the same second')
+  expect(+m).toEqual(+mCopy, 'isAfter second should not change moment')
+})
+
+test('is after millisecond', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'millisecond match')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'milliseconds')).toBe(true, 'plural should work')
+  expect(m.isAfter(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'month is later')
+  expect(m.isAfter(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'month is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'day is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 1, 4, 5, 10)), 'millisecond')).toBe(true, 'day is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)), 'millisecond')).toBe(false, 'hour is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 4, 1, 5, 10)), 'millisecond')).toBe(true, 'hour is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)), 'millisecond')).toBe(false, 'minute is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)), 'millisecond')).toBe(true, 'minute is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)), 'millisecond')).toBe(false, 'second is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 5)), 'millisecond')).toBe(true, 'second is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 11)), 'millisecond')).toBe(false, 'millisecond is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 9)), 'millisecond')).toBe(true, 'millisecond is earlier')
+  expect(m.isAfter(m, 'millisecond')).toBe(false, 'same moments are not after the same millisecond')
+  expect(+m).toEqual(+mCopy, 'isAfter millisecond should not change moment')
+})
+
+
+test('is after without units', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isAfter(dayjs(new Date(2012, 3, 2, 3, 5, 5, 10)))).toBe(false, 'year is later')
+  expect(m.isAfter(dayjs(new Date(2010, 3, 2, 3, 3, 5, 10)))).toBe(true, 'year is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)))).toBe(false, 'month is later')
+  expect(m.isAfter(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)))).toBe(true, 'month is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)))).toBe(false, 'day is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 1, 3, 4, 5, 10)))).toBe(true, 'day is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)))).toBe(false, 'hour is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 2, 4, 5, 10)))).toBe(true, 'hour is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)))).toBe(false, 'minute is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)))).toBe(true, 'minute is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)))).toBe(false, 'second is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 11)))).toBe(true, 'second is earlier')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)))).toBe(false, 'millisecond match')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 11)))).toBe(false, 'millisecond is later')
+  expect(m.isAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 9)))).toBe(true, 'millisecond is earlier')
+  expect(m.isAfter(m)).toBe(false, 'moments are not after themselves')
+  expect(+m).toEqual(+mCopy, 'isAfter second should not change moment')
+})
+
+test('is after invalid', () => {
+  const m = dayjs()
+  const invalid = dayjs(null)
+  expect(m.isAfter(invalid)).toBe(false, 'valid moment is not after invalid moment')
+  expect(invalid.isAfter(m)).toBe(false, 'invalid moment is not after valid moment')
+  expect(m.isAfter(invalid, 'year')).toBe(false, 'invalid moment year')
+  expect(m.isAfter(invalid, 'month')).toBe(false, 'invalid moment month')
+  expect(m.isAfter(invalid, 'day')).toBe(false, 'invalid moment day')
+  expect(m.isAfter(invalid, 'hour')).toBe(false, 'invalid moment hour')
+  expect(m.isAfter(invalid, 'minute')).toBe(false, 'invalid moment minute')
+  expect(m.isAfter(invalid, 'second')).toBe(false, 'invalid moment second')
+  expect(m.isAfter(invalid, 'milliseconds')).toBe(false, 'invalid moment milliseconds')
+})
+
+// isBefore()
+
+test('is after without units', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 5, 5, 10)))).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 2, 3, 3, 5, 10)))).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)))).toBe(true, 'month is later')
+  expect(m.isBefore(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)))).toBe(false, 'month is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)))).toBe(true, 'day is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 3, 4, 5, 10)))).toBe(false, 'day is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)))).toBe(true, 'hour is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 2, 4, 5, 10)))).toBe(false, 'hour is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)))).toBe(true, 'minute is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)))).toBe(false, 'minute is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)))).toBe(true, 'second is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 11)))).toBe(false, 'second is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)))).toBe(false, 'millisecond match')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 11)))).toBe(true, 'millisecond is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 9)))).toBe(false, 'millisecond is earlier')
+  expect(m.isBefore(m)).toBe(false, 'moments are not before themselves')
+  expect(+m).toEqual(+mCopy, 'isBefore second should not change moment')
+})
+
+test('is before year', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'year')).toBe(false, 'year match')
+  expect(m.isBefore(dayjs(new Date(2012, 5, 6, 7, 8, 9, 10)), 'years')).toBe(true, 'plural should work')
+  expect(m.isBefore(dayjs(new Date(2013, 5, 6, 7, 8, 9, 10)), 'year')).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)), 'year')).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 0, 1, 0, 0, 0, 0)), 'year')).toBe(false, 'exact start of year')
+  expect(m.isBefore(dayjs(new Date(2011, 11, 31, 23, 59, 59, 999)), 'year')).toBe(false, 'exact end of year')
+  expect(m.isBefore(dayjs(new Date(2012, 0, 1, 0, 0, 0, 0)), 'year')).toBe(true, 'start of next year')
+  expect(m.isBefore(dayjs(new Date(2010, 11, 31, 23, 59, 59, 999)), 'year')).toBe(false, 'end of previous year')
+  expect(m.isBefore(dayjs(new Date(1980, 11, 31, 23, 59, 59, 999)), 'year')).toBe(false, 'end of year far before')
+  expect(m.isBefore(m, 'year')).toBe(false, 'same moments are not before the same year')
+  expect(+m).toEqual(+mCopy, 'isBefore year should not change moment')
+})
+
+test('is before month', () => {
+  const m = dayjs(new Date(2011, 2, 3, 4, 5, 6, 7))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'month match')
+  expect(m.isBefore(dayjs(new Date(2012, 2, 6, 7, 8, 9, 10)), 'months')).toBe(true, 'plural should work')
+  expect(m.isBefore(dayjs(new Date(2012, 2, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 2, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'month is later')
+  expect(m.isBefore(dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'month is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 2, 1, 0, 0, 0, 0)), 'month')).toBe(false, 'exact start of month')
+  expect(m.isBefore(dayjs(new Date(2011, 2, 31, 23, 59, 59, 999)), 'month')).toBe(false, 'exact end of month')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 0, 0, 0, 0)), 'month')).toBe(true, 'start of next month')
+  expect(m.isBefore(dayjs(new Date(2011, 1, 27, 23, 59, 59, 999)), 'month')).toBe(false, 'end of previous month')
+  expect(m.isBefore(dayjs(new Date(2010, 12, 31, 23, 59, 59, 999)), 'month')).toBe(false, 'later month but earlier year')
+  expect(m.isBefore(m, 'month')).toBe(false, 'same moments are not before the same month')
+  expect(+m).toEqual(+mCopy, 'isBefore month should not change moment')
+})
+
+test('is before day', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'day match')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 7, 8, 9, 10)), 'days')).toBe(true, 'plural should work')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 4, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'month is later')
+  expect(m.isBefore(dayjs(new Date(2011, 2, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'month is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 3, 7, 8, 9, 10)), 'day')).toBe(true, 'day is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 7, 8, 9, 10)), 'day')).toBe(false, 'day is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 0, 0, 0, 0)), 'day')).toBe(false, 'exact start of day')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 23, 59, 59, 999)), 'day')).toBe(false, 'exact end of day')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 3, 0, 0, 0, 0)), 'day')).toBe(true, 'start of next day')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 23, 59, 59, 999)), 'day')).toBe(false, 'end of previous day')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 10, 0, 0, 0, 0)), 'day')).toBe(false, 'later day but earlier year')
+  expect(m.isBefore(m, 'day')).toBe(false, 'same moments are not before the same day')
+  expect(+m).toEqual(+mCopy, 'isBefore day should not change moment')
+})
+
+test('is before hour', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'hour match')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 8, 9, 10)), 'hours')).toBe(true, 'plural should work')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 4, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'month is later')
+  expect(m.isBefore(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'month is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 3, 3, 8, 9, 10)), 'hour')).toBe(true, 'day is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 3, 8, 9, 10)), 'hour')).toBe(false, 'day is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 4, 8, 9, 10)), 'hour')).toBe(true, 'hour is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'hour is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 0, 0, 0)), 'hour')).toBe(false, 'exact start of hour')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 59, 59, 999)), 'hour')).toBe(false, 'exact end of hour')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 4, 0, 0, 0)), 'hour')).toBe(true, 'start of next hour')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 2, 59, 59, 999)), 'hour')).toBe(false, 'end of previous hour')
+  expect(m.isBefore(m, 'hour')).toBe(false, 'same moments are not before the same hour')
+  expect(+m).toEqual(+mCopy, 'isBefore hour should not change moment')
+})
+
+test('is before minute', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'minute match')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 4, 9, 10)), 'minutes')).toBe(true, 'plural should work')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 4, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'month is later')
+  expect(m.isBefore(dayjs(new Date(2011, 2, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'month is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 3, 3, 4, 9, 10)), 'minute')).toBe(true, 'day is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 3, 4, 9, 10)), 'minute')).toBe(false, 'day is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 4, 4, 9, 10)), 'minute')).toBe(true, 'hour is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 2, 4, 9, 10)), 'minute')).toBe(false, 'hour is earler')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 5, 9, 10)), 'minute')).toBe(true, 'minute is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 3, 9, 10)), 'minute')).toBe(false, 'minute is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 0, 0)), 'minute')).toBe(false, 'exact start of minute')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 59, 999)), 'minute')).toBe(false, 'exact end of minute')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 5, 0, 0)), 'minute')).toBe(true, 'start of next minute')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 3, 59, 999)), 'minute')).toBe(false, 'end of previous minute')
+  expect(m.isBefore(m, 'minute')).toBe(false, 'same moments are not before the same minute')
+  expect(+m).toEqual(+mCopy, 'isBefore minute should not change moment')
+})
+
+test('is before second', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'second match')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'seconds')).toBe(true, 'plural should work')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'month is later')
+  expect(m.isBefore(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'month is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)), 'second')).toBe(true, 'day is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 1, 4, 5, 10)), 'second')).toBe(false, 'day is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)), 'second')).toBe(true, 'hour is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 4, 1, 5, 10)), 'second')).toBe(false, 'hour is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)), 'second')).toBe(true, 'minute is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)), 'second')).toBe(false, 'minute is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)), 'second')).toBe(true, 'second is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 5)), 'second')).toBe(false, 'second is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 0)), 'second')).toBe(false, 'exact start of second')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 999)), 'second')).toBe(false, 'exact end of second')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 0)), 'second')).toBe(true, 'start of next second')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 999)), 'second')).toBe(false, 'end of previous second')
+  expect(m.isBefore(m, 'second')).toBe(false, 'same moments are not before the same second')
+  expect(+m).toEqual(+mCopy, 'isBefore second should not change moment')
+})
+
+test('is before millisecond', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'millisecond match')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'milliseconds')).toBe(false, 'plural should work')
+  expect(m.isBefore(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'year is later')
+  expect(m.isBefore(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'year is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'month is later')
+  expect(m.isBefore(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'month is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'day is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 1, 4, 5, 10)), 'millisecond')).toBe(false, 'day is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)), 'millisecond')).toBe(true, 'hour is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 1, 4, 1, 5, 10)), 'millisecond')).toBe(false, 'hour is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)), 'millisecond')).toBe(true, 'minute is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)), 'millisecond')).toBe(false, 'minute is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)), 'millisecond')).toBe(true, 'second is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 5)), 'millisecond')).toBe(false, 'second is earlier')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 11)), 'millisecond')).toBe(true, 'millisecond is later')
+  expect(m.isBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 9)), 'millisecond')).toBe(false, 'millisecond is earlier')
+  expect(m.isBefore(m, 'millisecond')).toBe(false, 'same moments are not before the same millisecond')
+  expect(+m).toEqual(+mCopy, 'isBefore millisecond should not change moment')
+})
+
+test('is before invalid', () => {
+  const m = dayjs()
+  const invalid = dayjs(null)
+  expect(m.isBefore(invalid)).toBe(false, 'valid moment is not before invalid moment')
+  expect(invalid.isBefore(m)).toBe(false, 'invalid moment is not before valid moment')
+  expect(m.isBefore(invalid, 'year')).toBe(false, 'invalid moment year')
+  expect(m.isBefore(invalid, 'month')).toBe(false, 'invalid moment month')
+  expect(m.isBefore(invalid, 'day')).toBe(false, 'invalid moment day')
+  expect(m.isBefore(invalid, 'hour')).toBe(false, 'invalid moment hour')
+  expect(m.isBefore(invalid, 'minute')).toBe(false, 'invalid moment minute')
+  expect(m.isBefore(invalid, 'second')).toBe(false, 'invalid moment second')
+  expect(m.isBefore(invalid, 'milliseconds')).toBe(false, 'invalid moment milliseconds')
+})
diff --git a/test/index.d.test.ts b/test/index.d.test.ts
index d4a4484..4e6edb4 100644
--- a/test/index.d.test.ts
+++ b/test/index.d.test.ts
@@ -70,4 +70,10 @@ dayjs().isSame(dayjs())
 
 dayjs().isAfter(dayjs())
 
+dayjs().isBefore(dayjs(), 'minutes')
+
+dayjs().isSame(dayjs(), 'hours')
+
+dayjs().isAfter(dayjs(), 'year')
+
 dayjs('2000-01-01').isLeapYear()
diff --git a/test/plugin/isBetween.test.js b/test/plugin/isBetween.test.js
index 1165bc8..347654d 100644
--- a/test/plugin/isBetween.test.js
+++ b/test/plugin/isBetween.test.js
@@ -1,6 +1,6 @@
 import MockDate from 'mockdate'
-import dayjs from '../../src'
-import isBetween from '../../src/plugin/isBetween'
+import dayjs from '../../src/index'
+import isBetween from '../../src/plugin/isBetween/index'
 
 dayjs.extend(isBetween)
 
@@ -12,8 +12,372 @@ afterEach(() => {
   MockDate.reset()
 })
 
-it('is between', () => {
-  expect(dayjs('2018-01-01').isBetween('2017-01-01', '2019-01-01')).toBeTruthy()
-  expect(dayjs('2018-01-01').isBetween('2019-01-01', '2017-01-01')).toBeTruthy()
-  expect(dayjs('2018-01-01').isBetween('2016-01-01', '2017-01-01')).toBeFalsy()
+test('bounds can be swapped', () => {
+  expect(dayjs('2018-01-01').isBetween(dayjs('2017-12-31'), dayjs('2018-01-02'))).toBeTruthy()
+  expect(dayjs('2018-01-01').isBetween(dayjs('2018-01-02'), dayjs('2017-12-31'))).toBeTruthy()
+})
+
+test('is between without units', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+
+  expect(m.isBetween(
+    dayjs(new Date(2009, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'year is later')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2013, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'year is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2012, 3, 2, 3, 4, 5, 10))
+  )).toBe(true, 'year is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'month is later')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 5, 2, 3, 4, 5, 10))
+  )).toBe(false, 'month is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 4, 2, 3, 4, 5, 10))
+  )).toBe(true, 'month is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 1, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'day is later')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 4, 3, 4, 5, 10))
+  )).toBe(false, 'day is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 1, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 3, 3, 4, 5, 10))
+  )).toBe(true, 'day is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 1, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'hour is later')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 5, 4, 5, 10))
+  )).toBe(false, 'hour is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 2, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 4, 4, 5, 10))
+  )).toBe(true, 'hour is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 6, 5, 10))
+  )).toBe(false, 'minute is later')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 2, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'minute is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 5, 5, 10))
+  )).toBe(true, 'minute is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 7, 10))
+  )).toBe(false, 'second is later')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 3, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'second is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 4, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 6, 10))
+  )).toBe(true, 'second is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 12))
+  )).toBe(false, 'millisecond is later')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 8)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  )).toBe(false, 'millisecond is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 9)),
+    dayjs(new Date(2011, 3, 2, 3, 4, 5, 11))
+  )).toBe(true, 'millisecond is between')
+
+  expect(m.isBetween(m, m)).toBe(false, 'moments are not between themselves')
+  expect(+m).toEqual(+mCopy, 'isBetween second should not change moment')
+})
+
+test('is between year', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)),
+    'year'
+  )).toBe(false, 'year match')
+
+  expect(m.isBetween(
+    dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2012, 5, 6, 7, 8, 9, 10)),
+    'years'
+  )).toBe(true, 'plural should work')
+
+  expect(m.isBetween(
+    dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2012, 5, 6, 7, 8, 9, 10)),
+    'year'
+  )).toBe(true, 'year is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2013, 5, 6, 7, 8, 9, 10)),
+    'year'
+  )).toBe(false, 'year is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)),
+    'year'
+  )).toBe(false, 'year is later')
+
+  expect(m.isBetween(m, 'year')).toBe(false, 'same moments are not between the same year')
+  expect(+m).toEqual(+mCopy, 'isBetween year should not change moment')
+})
+
+test('is between month', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)),
+    'month'
+  )).toBe(false, 'month match')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 0, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)),
+    'months'
+  )).toBe(true, 'plural should work')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 0, 31, 23, 59, 59, 999)),
+    dayjs(new Date(2011, 2, 1, 0, 0, 0, 0)),
+    'month'
+  )).toBe(true, 'month is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)),
+    'month'
+  )).toBe(false, 'month is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 11, 6, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)),
+    'month'
+  )).toBe(false, 'month is later')
+
+  expect(m.isBetween(m, 'month')).toBe(false, 'same moments are not between the same month')
+  expect(+m).toEqual(+mCopy, 'isBetween month should not change moment')
+})
+
+test('is between day', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)),
+    'day'
+  )).toBe(false, 'day match')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 1, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 3, 7, 8, 9, 10)),
+    'days'
+  )).toBe(true, 'plural should work')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 1, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 3, 7, 8, 9, 10)),
+    'day'
+  )).toBe(true, 'day is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 4, 7, 8, 9, 10)),
+    'day'
+  )).toBe(false, 'day is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 1, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)),
+    'day'
+  )).toBe(false, 'day is later')
+
+  expect(m.isBetween(m, 'day')).toBe(false, 'same moments are not between the same day')
+  expect(+m).toEqual(+mCopy, 'isBetween day should not change moment')
+})
+
+test('is between hour', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 5, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 3, 9, 9, 10)), 'hour'
+  ))
+    .toBe(false, 'hour match')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 1, 59, 59, 999)),
+    dayjs(new Date(2011, 1, 2, 4, 0, 0, 0)), 'hours'
+  ))
+    .toBe(true, 'plural should work')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 2, 59, 59, 999)),
+    dayjs(new Date(2011, 1, 2, 4, 0, 0, 0)), 'hour'
+  ))
+    .toBe(true, 'hour is between')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'hour'
+  ))
+    .toBe(false, 'hour is earlier')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'hour'
+  ))
+    .toBe(false, 'hour is later')
+  expect(m.isBetween(m, 'hour'))
+    .toBe(false, 'same moments are not between the same hour')
+  expect(+m)
+    .toEqual(+mCopy, 'isBetween hour should not change moment')
+})
+
+test('is between minute', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)), 'minute'
+  ))
+    .toBe(false, 'minute match')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 3, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 3, 5, 9, 10)), 'minutes'
+  ))
+    .toBe(true, 'plural should work')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 3, 59, 999)),
+    dayjs(new Date(2011, 1, 2, 3, 5, 0, 0)), 'minute'
+  ))
+    .toBe(true, 'minute is between')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 5, 0, 0)),
+    dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'minute'
+  ))
+    .toBe(false, 'minute is earlier')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 2, 9, 10)),
+    dayjs(new Date(2011, 1, 2, 3, 3, 59, 999)), 'minute'
+  ))
+    .toBe(false, 'minute is later')
+  expect(m.isBetween(m, 'minute'))
+    .toBe(false, 'same moments are not between the same minute')
+  expect(+m)
+    .toEqual(+mCopy, 'isBetween minute should not change moment')
+})
+
+test('is between second', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)), 'second'
+  ))
+    .toBe(false, 'second match')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 4, 10)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 6, 10)), 'seconds'
+  ))
+    .toBe(true, 'plural should work')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 4, 999)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 6, 0)), 'second'
+  ))
+    .toBe(true, 'second is between')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 6, 0)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 7, 10)), 'second'
+  ))
+    .toBe(false, 'second is earlier')
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 3, 10)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 4, 999)), 'second'
+  ))
+    .toBe(false, 'second is later')
+  expect(m.isBetween(m, 'second')).toBe(false, 'same moments are not between the same second')
+  expect(+m).toEqual(+mCopy, 'isBetween second should not change moment')
+})
+
+test('is between millisecond', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 6)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 6)),
+    'millisecond'
+  )).toBe(false, 'millisecond match')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 5)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 7)),
+    'milliseconds'
+  )).toBe(true, 'plural should work')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 5)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 7)),
+    'millisecond'
+  )).toBe(true, 'millisecond is between')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 7)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)),
+    'millisecond'
+  )).toBe(false, 'millisecond is earlier')
+
+  expect(m.isBetween(
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 4)),
+    dayjs(new Date(2011, 1, 2, 3, 4, 5, 6)),
+    'millisecond'
+  )).toBe(false, 'millisecond is later')
+
+  expect(m.isBetween(m, 'millisecond')).toBe(false, 'same moments are not between the same millisecond')
+  expect(+m).toEqual(+mCopy, 'isBetween millisecond should not change moment')
 })
diff --git a/test/plugin/isSameOrAfter.test.js b/test/plugin/isSameOrAfter.test.js
new file mode 100644
index 0000000..fd504c2
--- /dev/null
+++ b/test/plugin/isSameOrAfter.test.js
@@ -0,0 +1,193 @@
+import MockDate from 'mockdate'
+import dayjs from '../../src'
+import isSameOrAfter from '../../src/plugin/isSameOrAfter'
+
+dayjs.extend(isSameOrAfter)
+
+beforeEach(() => {
+  MockDate.set(new Date())
+})
+
+afterEach(() => {
+  MockDate.reset()
+})
+
+test('is same or after year', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'year')).toBe(true, 'year match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'years')).toBe(true, 'plural should work')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 5, 6, 7, 8, 9, 10)), 'year')).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)), 'year')).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 0, 1, 0, 0, 0, 0)), 'year')).toBe(true, 'exact start of year')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 11, 31, 23, 59, 59, 999)), 'year')).toBe(true, 'exact end of year')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 0, 1, 0, 0, 0, 0)), 'year')).toBe(false, 'start of next year')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 11, 31, 23, 59, 59, 999)), 'year')).toBe(true, 'end of previous year')
+  expect(m.isSameOrAfter(m, 'year')).toBe(true, 'same moments are in the same year')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter year should not change moment')
+})
+
+test('is same or after without units', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 3, 2, 3, 5, 5, 10)))).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 3, 2, 3, 3, 5, 10)))).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)))).toBe(false, 'month is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)))).toBe(true, 'month is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)))).toBe(false, 'day is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 1, 3, 4, 5, 10)))).toBe(true, 'day is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)))).toBe(false, 'hour is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 2, 4, 5, 10)))).toBe(true, 'hour is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)))).toBe(false, 'minute is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)))).toBe(true, 'minute is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)))).toBe(false, 'second is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 11)))).toBe(true, 'second is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)))).toBe(true, 'millisecond match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 11)))).toBe(false, 'millisecond is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 9)))).toBe(true, 'millisecond is earlier')
+  expect(m.isSameOrAfter(m)).toBe(true, 'moments are the same as themselves')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter second should not change moment')
+})
+
+test('is same or after month', () => {
+  const m = dayjs(new Date(2011, 2, 3, 4, 5, 6, 7))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'month match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'months')).toBe(true, 'plural should work')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 2, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 2, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'month is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'month is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 1, 0, 0, 0, 0)), 'month')).toBe(true, 'exact start of month')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 31, 23, 59, 59, 999)), 'month')).toBe(true, 'exact end of month')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 1, 0, 0, 0, 0)), 'month')).toBe(false, 'start of next month')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 27, 23, 59, 59, 999)), 'month')).toBe(true, 'end of previous month')
+  expect(m.isSameOrAfter(m, 'month')).toBe(true, 'same moments are in the same month')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter month should not change moment')
+})
+
+test('is same or after day', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'day match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'days')).toBe(true, 'plural should work')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 1, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 1, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'month is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 12, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'month is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 3, 7, 8, 9, 10)), 'day')).toBe(false, 'day is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 1, 7, 8, 9, 10)), 'day')).toBe(true, 'day is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 0, 0, 0, 0)), 'day')).toBe(true, 'exact start of day')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 23, 59, 59, 999)), 'day')).toBe(true, 'exact end of day')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 3, 0, 0, 0, 0)), 'day')).toBe(false, 'start of next day')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 1, 23, 59, 59, 999)), 'day')).toBe(true, 'end of previous day')
+  expect(m.isSameOrAfter(m, 'day')).toBe(true, 'same moments are in the same day')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter day should not change moment')
+})
+
+test('is same or after hour', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'hour match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hours')).toBe(true, 'plural should work')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'month is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 12, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'month is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 3, 3, 8, 9, 10)), 'hour')).toBe(false, 'day is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 1, 3, 8, 9, 10)), 'hour')).toBe(true, 'day is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 4, 8, 9, 10)), 'hour')).toBe(false, 'hour is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 2, 8, 9, 10)), 'hour')).toBe(true, 'hour is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 0, 0, 0)), 'hour')).toBe(true, 'exact start of hour')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 59, 59, 999)), 'hour')).toBe(true, 'exact end of hour')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 4, 0, 0, 0)), 'hour')).toBe(false, 'start of next hour')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 2, 59, 59, 999)), 'hour')).toBe(true, 'end of previous hour')
+  expect(m.isSameOrAfter(m, 'hour')).toBe(true, 'same moments are in the same hour')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter hour should not change moment')
+})
+
+test('is same or after minute', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'minute match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)), 'minutes')).toBe(true, 'plural should work')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'month is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 12, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'month is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 3, 3, 4, 9, 10)), 'minute')).toBe(false, 'day is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 1, 3, 4, 9, 10)), 'minute')).toBe(true, 'day is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 4, 4, 9, 10)), 'minute')).toBe(false, 'hour is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 2, 4, 9, 10)), 'minute')).toBe(true, 'hour is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 5, 9, 10)), 'minute')).toBe(false, 'minute is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 3, 9, 10)), 'minute')).toBe(true, 'minute is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 0, 0)), 'minute')).toBe(true, 'exact start of minute')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 59, 999)), 'minute')).toBe(true, 'exact end of minute')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 5, 0, 0)), 'minute')).toBe(false, 'start of next minute')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 3, 59, 999)), 'minute')).toBe(true, 'end of previous minute')
+  expect(m.isSameOrAfter(m, 'minute')).toBe(true, 'same moments are in the same minute')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter minute should not change moment')
+})
+
+test('is same or after second', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'second match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)), 'seconds')).toBe(true, 'plural should work')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 1, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 1, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'month is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 12, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'month is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 3, 3, 4, 5, 10)), 'second')).toBe(false, 'day is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 1, 3, 4, 5, 10)), 'second')).toBe(true, 'day is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 4, 4, 5, 10)), 'second')).toBe(false, 'hour is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 2, 4, 5, 10)), 'second')).toBe(true, 'hour is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 5, 5, 10)), 'second')).toBe(false, 'minute is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 3, 5, 10)), 'second')).toBe(true, 'minute is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 6, 10)), 'second')).toBe(false, 'second is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 4, 10)), 'second')).toBe(true, 'second is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 5, 0)), 'second')).toBe(true, 'exact start of second')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 5, 999)), 'second')).toBe(true, 'exact end of second')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 6, 0)), 'second')).toBe(false, 'start of next second')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 1, 2, 3, 4, 4, 999)), 'second')).toBe(true, 'end of previous second')
+  expect(m.isSameOrAfter(m, 'second')).toBe(true, 'same moments are in the same second')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter second should not change moment')
+})
+
+test('is same or after millisecond', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'millisecond match')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'milliseconds')).toBe(true, 'plural should work')
+  expect(m.isSameOrAfter(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'year is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'year is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'month is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'month is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'day is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 1, 1, 4, 5, 10)), 'millisecond')).toBe(true, 'day is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)), 'millisecond')).toBe(false, 'hour is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 1, 4, 1, 5, 10)), 'millisecond')).toBe(true, 'hour is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)), 'millisecond')).toBe(false, 'minute is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)), 'millisecond')).toBe(true, 'minute is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)), 'millisecond')).toBe(false, 'second is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 5)), 'millisecond')).toBe(true, 'second is earlier')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 6, 11)), 'millisecond')).toBe(false, 'millisecond is later')
+  expect(m.isSameOrAfter(dayjs(new Date(2011, 3, 2, 3, 4, 4, 9)), 'millisecond')).toBe(true, 'millisecond is earlier')
+  expect(m.isSameOrAfter(m, 'millisecond')).toBe(true, 'same moments are in the same millisecond')
+  expect(+m).toEqual(+mCopy, 'isSameOrAfter millisecond should not change moment')
+})
+
+test('is same or after with invalid moments', () => {
+  const m = dayjs()
+  const invalid = dayjs(null)
+  expect(invalid.isSameOrAfter(invalid)).toBe(false, 'invalid moments are not considered equal')
+  expect(m.isSameOrAfter(invalid)).toBe(false, 'valid moment is not after invalid moment')
+  expect(invalid.isSameOrAfter(m)).toBe(false, 'invalid moment is not after valid moment')
+  expect(m.isSameOrAfter(invalid, 'year')).toBe(false, 'invalid moment year')
+  expect(m.isSameOrAfter(invalid, 'month')).toBe(false, 'invalid moment month')
+  expect(m.isSameOrAfter(invalid, 'day')).toBe(false, 'invalid moment day')
+  expect(m.isSameOrAfter(invalid, 'hour')).toBe(false, 'invalid moment hour')
+  expect(m.isSameOrAfter(invalid, 'minute')).toBe(false, 'invalid moment minute')
+  expect(m.isSameOrAfter(invalid, 'second')).toBe(false, 'invalid moment second')
+  expect(m.isSameOrAfter(invalid, 'milliseconds')).toBe(false, 'invalid moment milliseconds')
+})
diff --git a/test/plugin/isSameOrBefore.test.js b/test/plugin/isSameOrBefore.test.js
new file mode 100644
index 0000000..a8e1821
--- /dev/null
+++ b/test/plugin/isSameOrBefore.test.js
@@ -0,0 +1,193 @@
+import MockDate from 'mockdate'
+import dayjs from '../../src'
+import isSameOrBefore from '../../src/plugin/isSameOrBefore'
+
+dayjs.extend(isSameOrBefore)
+
+beforeEach(() => {
+  MockDate.set(new Date())
+})
+
+afterEach(() => {
+  MockDate.reset()
+})
+
+test('is same or before without units', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 3, 2, 3, 5, 5, 10)))).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 3, 2, 3, 3, 5, 10)))).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)))).toBe(true, 'month is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)))).toBe(false, 'month is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)))).toBe(true, 'day is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 1, 3, 4, 5, 10)))).toBe(false, 'day is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)))).toBe(true, 'hour is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 2, 4, 5, 10)))).toBe(false, 'hour is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)))).toBe(true, 'minute is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)))).toBe(false, 'minute is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)))).toBe(true, 'second is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 11)))).toBe(false, 'second is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)))).toBe(true, 'millisecond match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 11)))).toBe(true, 'millisecond is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 9)))).toBe(false, 'millisecond is earlier')
+  expect(m.isSameOrBefore(m)).toBe(true, 'moments are the same as themselves')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore second should not change moment')
+})
+
+test('is same or before year', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'year')).toBe(true, 'year match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'years')).toBe(true, 'plural should work')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 5, 6, 7, 8, 9, 10)), 'year')).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 5, 6, 7, 8, 9, 10)), 'year')).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 0, 1, 0, 0, 0, 0)), 'year')).toBe(true, 'exact start of year')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 11, 31, 23, 59, 59, 999)), 'year')).toBe(true, 'exact end of year')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 0, 1, 0, 0, 0, 0)), 'year')).toBe(true, 'start of next year')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 11, 31, 23, 59, 59, 999)), 'year')).toBe(false, 'end of previous year')
+  expect(m.isSameOrBefore(m, 'year')).toBe(true, 'same moments are in the same year')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore year should not change moment')
+})
+
+test('is same or before month', () => {
+  const m = dayjs(new Date(2011, 2, 3, 4, 5, 6, 7))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'month match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 6, 7, 8, 9, 10)), 'months')).toBe(true, 'plural should work')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 2, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 2, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 5, 6, 7, 8, 9, 10)), 'month')).toBe(true, 'month is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 6, 7, 8, 9, 10)), 'month')).toBe(false, 'month is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 1, 0, 0, 0, 0)), 'month')).toBe(true, 'exact start of month')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 31, 23, 59, 59, 999)), 'month')).toBe(true, 'exact end of month')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 1, 0, 0, 0, 0)), 'month')).toBe(true, 'start of next month')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 27, 23, 59, 59, 999)), 'month')).toBe(false, 'end of previous month')
+  expect(m.isSameOrBefore(m, 'month')).toBe(true, 'same moments are in the same month')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore month should not change moment')
+})
+
+test('is same or before day', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'day match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 7, 8, 9, 10)), 'days')).toBe(true, 'plural should work')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 1, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 1, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 2, 7, 8, 9, 10)), 'day')).toBe(true, 'month is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 12, 2, 7, 8, 9, 10)), 'day')).toBe(false, 'month is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 3, 7, 8, 9, 10)), 'day')).toBe(true, 'day is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 1, 7, 8, 9, 10)), 'day')).toBe(false, 'day is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 0, 0, 0, 0)), 'day')).toBe(true, 'exact start of day')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 23, 59, 59, 999)), 'day')).toBe(true, 'exact end of day')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 3, 0, 0, 0, 0)), 'day')).toBe(true, 'start of next day')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 1, 23, 59, 59, 999)), 'day')).toBe(false, 'end of previous day')
+  expect(m.isSameOrBefore(m, 'day')).toBe(true, 'same moments are in the same day')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore day should not change moment')
+})
+
+test('is same or before hour', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'hour match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 8, 9, 10)), 'hours')).toBe(true, 'plural should work')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 1, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 2, 3, 8, 9, 10)), 'hour')).toBe(true, 'month is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 12, 2, 3, 8, 9, 10)), 'hour')).toBe(false, 'month is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 3, 3, 8, 9, 10)), 'hour')).toBe(true, 'day is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 1, 3, 8, 9, 10)), 'hour')).toBe(false, 'day is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 4, 8, 9, 10)), 'hour')).toBe(true, 'hour is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 2, 8, 9, 10)), 'hour')).toBe(false, 'hour is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 0, 0, 0)), 'hour')).toBe(true, 'exact start of hour')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 59, 59, 999)), 'hour')).toBe(true, 'exact end of hour')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 4, 0, 0, 0)), 'hour')).toBe(true, 'start of next hour')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 2, 59, 59, 999)), 'hour')).toBe(false, 'end of previous hour')
+  expect(m.isSameOrBefore(m, 'hour')).toBe(true, 'same moments are in the same hour')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore hour should not change moment')
+})
+
+test('is same or before minute', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'minute match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 9, 10)), 'minutes')).toBe(true, 'plural should work')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 1, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 2, 3, 4, 9, 10)), 'minute')).toBe(true, 'month is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 12, 2, 3, 4, 9, 10)), 'minute')).toBe(false, 'month is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 3, 3, 4, 9, 10)), 'minute')).toBe(true, 'day is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 1, 3, 4, 9, 10)), 'minute')).toBe(false, 'day is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 4, 4, 9, 10)), 'minute')).toBe(true, 'hour is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 2, 4, 9, 10)), 'minute')).toBe(false, 'hour is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 5, 9, 10)), 'minute')).toBe(true, 'minute is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 3, 9, 10)), 'minute')).toBe(false, 'minute is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 0, 0)), 'minute')).toBe(true, 'exact start of minute')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 59, 999)), 'minute')).toBe(true, 'exact end of minute')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 5, 0, 0)), 'minute')).toBe(true, 'start of next minute')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 3, 59, 999)), 'minute')).toBe(false, 'end of previous minute')
+  expect(m.isSameOrBefore(m, 'minute')).toBe(true, 'same moments are in the same minute')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore minute should not change moment')
+})
+
+test('is same or before second', () => {
+  const m = dayjs(new Date(2011, 1, 2, 3, 4, 5, 6))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'second match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 5, 10)), 'seconds')).toBe(true, 'plural should work')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 1, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 1, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'second')).toBe(true, 'month is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 12, 2, 3, 4, 5, 10)), 'second')).toBe(false, 'month is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 3, 3, 4, 5, 10)), 'second')).toBe(true, 'day is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 1, 3, 4, 5, 10)), 'second')).toBe(false, 'day is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 4, 4, 5, 10)), 'second')).toBe(true, 'hour is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 2, 4, 5, 10)), 'second')).toBe(false, 'hour is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 5, 5, 10)), 'second')).toBe(true, 'minute is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 3, 5, 10)), 'second')).toBe(false, 'minute is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 6, 10)), 'second')).toBe(true, 'second is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 4, 10)), 'second')).toBe(false, 'second is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 5, 0)), 'second')).toBe(true, 'exact start of second')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 5, 999)), 'second')).toBe(true, 'exact end of second')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 6, 0)), 'second')).toBe(true, 'start of next second')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 1, 2, 3, 4, 4, 999)), 'second')).toBe(false, 'end of previous second')
+  expect(m.isSameOrBefore(m, 'second')).toBe(true, 'same moments are in the same second')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore second should not change moment')
+})
+
+test('is same or before millisecond', () => {
+  const m = dayjs(new Date(2011, 3, 2, 3, 4, 5, 10))
+  const mCopy = dayjs(m)
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'millisecond match')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 5, 10)), 'milliseconds')).toBe(true, 'plural should work')
+  expect(m.isSameOrBefore(dayjs(new Date(2012, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'year is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2010, 3, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'year is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 4, 2, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'month is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 2, 2, 3, 4, 5, 10)), 'millisecond')).toBe(false, 'month is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 3, 3, 4, 5, 10)), 'millisecond')).toBe(true, 'day is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 1, 1, 4, 5, 10)), 'millisecond')).toBe(false, 'day is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 4, 4, 5, 10)), 'millisecond')).toBe(true, 'hour is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 1, 4, 1, 5, 10)), 'millisecond')).toBe(false, 'hour is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 5, 5, 10)), 'millisecond')).toBe(true, 'minute is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 3, 5, 10)), 'millisecond')).toBe(false, 'minute is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 10)), 'millisecond')).toBe(true, 'second is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 5)), 'millisecond')).toBe(false, 'second is earlier')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 6, 11)), 'millisecond')).toBe(true, 'millisecond is later')
+  expect(m.isSameOrBefore(dayjs(new Date(2011, 3, 2, 3, 4, 4, 9)), 'millisecond')).toBe(false, 'millisecond is earlier')
+  expect(m.isSameOrBefore(m, 'millisecond')).toBe(true, 'same moments are in the same millisecond')
+  expect(+m).toEqual(+mCopy, 'isSameOrBefore millisecond should not change moment')
+})
+
+test('is same with invalid moments', () => {
+  const m = dayjs()
+  const invalid = dayjs(null)
+  expect(invalid.isSameOrBefore(invalid)).toBe(false, 'invalid moments are not considered equal')
+  expect(m.isSameOrBefore(invalid)).toBe(false, 'valid moment is not before invalid moment')
+  expect(invalid.isSameOrBefore(m)).toBe(false, 'invalid moment is not before valid moment')
+  expect(m.isSameOrBefore(invalid, 'year')).toBe(false, 'invalid moment year')
+  expect(m.isSameOrBefore(invalid, 'month')).toBe(false, 'invalid moment month')
+  expect(m.isSameOrBefore(invalid, 'day')).toBe(false, 'invalid moment day')
+  expect(m.isSameOrBefore(invalid, 'hour')).toBe(false, 'invalid moment hour')
+  expect(m.isSameOrBefore(invalid, 'minute')).toBe(false, 'invalid moment minute')
+  expect(m.isSameOrBefore(invalid, 'second')).toBe(false, 'invalid moment second')
+  expect(m.isSameOrBefore(invalid, 'milliseconds')).toBe(false, 'invalid moment milliseconds')
+})