fix: UTC plugin set utcOffset value
close #620


<!-- Issuehunt content -->

---

<details>
<summary>
<b>IssueHunt Summary</b>
</summary>

### Referenced issues

This pull request has been submitted to:
- [#620: Setting UTCOffset](https://issuehunt.io/repos/128907699/issues/620)
---

IssueHunt has been backed by the following sponsors. [Become a sponsor](https://issuehunt.io/membership/members)
</details>
<!-- /Issuehunt content-->
<!-- Issuehunt Badges -->
[<img alt="Issuehunt badges" src="https://img.shields.io/badge/IssueHunt-%24100%20Rewarded-%237E24E3.svg" />](https://issuehunt.io/r/iamkun/dayjs/issues/620)
<!-- /Issuehunt Badges -->


I'm currently converting from `moment.js`, and was curious how to set the value of `utcOffset()`?

Here's an example of my code where you can see how it was being used with `moment.js` and the example beneath is my `day.js` conversion however, I can't set the `utcOffset` properly whereas my `set` methods are currently doing their job as they should be in the `day.js` conversion code.

**moment.js**
```javascript
moment(currentDate).utcOffset(0).set({hour: 0, minute: 0, second: 0, millisecond: 0})
```

**current day.js conversion work**
```javascript
dayjs(currentDate).set('hour', 0).set('minute', 0).set('second', 0).set('millisecond', 0)
```


<!-- Issuehunt content -->

---

<details>
<summary>
<b>IssueHunt Summary</b>
</summary>

#### [<img src='https://avatars0.githubusercontent.com/u/17680888?v=4' alt='iamkun' width=24 height=24> iamkun](https://issuehunt.io/u/iamkun) has been rewarded.

### Backers (Total: $100.00)

- [<img src='https://avatars3.githubusercontent.com/u/44827199?v=4' alt='issuehunt' width=24 height=24> issuehunt](https://issuehunt.io/u/issuehunt) ($100.00)
### Submitted pull Requests
- [#668 fix: UTC plugin set utcOffset value](https://issuehunt.io/r/iamkun/dayjs/pull/668)
---

### Tips

- Checkout the [Issuehunt explorer](https://issuehunt.io/r/iamkun/dayjs/) to discover more funded issues.
- Need some help from other developers? [Add your repositories](https://issuehunt.io/r/new) on IssueHunt to raise funds.
---
IssueHunt has been backed by the following sponsors. [Become a sponsor](https://issuehunt.io/membership/members)
</details>
<!-- /Issuehunt content-->
Setting UTCOffset
diff --git a/src/index.js b/src/index.js
index 434f60362..71b76b8a3 100644
--- a/src/index.js
+++ b/src/index.js
@@ -38,7 +38,8 @@ const dayjs = (date, c, pl) => {
   return new Dayjs(cfg) // eslint-disable-line no-use-before-define
 }
 
-const wrapper = (date, instance) => dayjs(date, { locale: instance.$L, utc: instance.$u })
+const wrapper = (date, instance) =>
+  dayjs(date, { locale: instance.$L, utc: instance.$u, $offset: instance.$offset })
 
 const Utils = U // for plugin use
 Utils.l = parseLocale
@@ -263,7 +264,7 @@ class Dayjs {
       [C.S]: C.MILLISECONDS_A_SECOND
     }[unit] || 1 // ms
 
-    const nextTimeStamp = this.valueOf() + (number * step)
+    const nextTimeStamp = this.$d.getTime() + (number * step)
     return Utils.w(nextTimeStamp, this)
   }
 
@@ -366,11 +367,11 @@ class Dayjs {
   }
 
   clone() {
-    return Utils.w(this.toDate(), this)
+    return Utils.w(this.$d, this)
   }
 
   toDate() {
-    return new Date(this.$d)
+    return new Date(this.valueOf())
   }
 
   toJSON() {
diff --git a/src/plugin/utc/index.js b/src/plugin/utc/index.js
index d367c97be..40c6a4959 100644
--- a/src/plugin/utc/index.js
+++ b/src/plugin/utc/index.js
@@ -1,4 +1,7 @@
+import { MILLISECONDS_A_MINUTE, MIN } from '../../constant'
+
 export default (option, Dayjs, dayjs) => {
+  const localOffset = (new Date()).getTimezoneOffset()
   const proto = Dayjs.prototype
   dayjs.utc = function (date, format) {
     const cfg = { date, utc: true, format }
@@ -18,6 +21,9 @@ export default (option, Dayjs, dayjs) => {
     if (cfg.utc) {
       this.$u = true
     }
+    if (!this.$utils().u(cfg.$offset)) {
+      this.$offset = cfg.$offset
+    }
     oldParse.call(this, cfg)
   }
 
@@ -39,11 +45,22 @@ export default (option, Dayjs, dayjs) => {
   }
 
   const oldUtcOffset = proto.utcOffset
-  proto.utcOffset = function () {
-    if (this.$u) {
-      return 0
+  proto.utcOffset = function (input) {
+    const { u } = this.$utils()
+    if (u(input)) {
+      if (this.$u) {
+        return 0
+      }
+      if (!u(this.$offset)) {
+        return this.$offset
+      }
+      return oldUtcOffset.call(this)
     }
-    return oldUtcOffset.call(this)
+    const offset = Math.abs(input) <= 16 ? input * 60 : input
+    const newD = this.add(offset + (this.$u ? 0 : localOffset), MIN)
+    newD.$offset = offset
+    newD.$u = input === 0 // UTC mode
+    return newD
   }
 
   const oldFormat = proto.format
@@ -53,7 +70,21 @@ export default (option, Dayjs, dayjs) => {
     return oldFormat.call(this, str)
   }
 
+  proto.valueOf = function () {
+    const addedOffset = !this.$utils().u(this.$offset)
+      ? this.$offset + localOffset : 0
+    return this.$d.valueOf() - (addedOffset * MILLISECONDS_A_MINUTE)
+  }
+
   proto.isUTC = function () {
     return !!this.$u
   }
+
+  proto.toISOString = function () {
+    return this.toDate().toISOString()
+  }
+
+  proto.toString = function () {
+    return this.toDate().toUTCString()
+  }
 }
diff --git a/types/plugin/utc.d.ts b/types/plugin/utc.d.ts
index 2f14efb22..d25e2e847 100644
--- a/types/plugin/utc.d.ts
+++ b/types/plugin/utc.d.ts
@@ -11,6 +11,8 @@ declare module 'dayjs' {
     local(): Dayjs
 
     isUTC(): boolean
+
+    utcOffset(offset: number): Dayjs
   }
 
   export function utc(config?: ConfigType, format?: string): Dayjs

diff --git a/test/plugin/utc-utcOffset.test.js b/test/plugin/utc-utcOffset.test.js
new file mode 100644
index 000000000..bd652be7a
--- /dev/null
+++ b/test/plugin/utc-utcOffset.test.js
@@ -0,0 +1,77 @@
+import MockDate from 'mockdate'
+import moment from 'moment'
+import dayjs from '../../src'
+import utc from '../../src/plugin/utc'
+
+dayjs.extend(utc)
+
+beforeEach(() => {
+  MockDate.set(new Date())
+})
+
+afterEach(() => {
+  MockDate.reset()
+})
+
+it('Set utcOffset -> Get utcOffset', () => {
+  expect(dayjs().utcOffset(540).utcOffset()).toBe(moment().utcOffset(540).utcOffset())
+  expect(dayjs().utcOffset(540).format()).toBe(moment().utcOffset(540).format())
+  expect(dayjs().utcOffset(60).format()).toBe(moment().utcOffset(60).format())
+  expect(dayjs().utcOffset(8).format()).toBe(moment().utcOffset(8).format())
+
+  expect(dayjs().utcOffset(-540).utcOffset()).toBe(moment().utcOffset(-540).utcOffset())
+  expect(dayjs().utcOffset(-540).format()).toBe(moment().utcOffset(-540).format())
+
+  expect(dayjs().utcOffset(-60).format()).toBe(moment().utcOffset(-60).format())
+  expect(dayjs().utcOffset(-8).format()).toBe(moment().utcOffset(-8).format())
+})
+
+it('valueOf, toDate, toString, toISOString should be the same as original', () => {
+  const d = dayjs()
+  const du = dayjs().utcOffset(9)
+  const mu = moment().utcOffset(9)
+  expect(d.valueOf()).toBe(du.valueOf())
+  expect(du.valueOf()).toBe(mu.valueOf())
+  expect(d.toDate()).toEqual(du.toDate())
+  expect(du.toDate()).toEqual(mu.toDate())
+  expect(du.toISOString()).toEqual(mu.toISOString())
+  expect(d.toString()).toEqual(d.toString())
+})
+
+it('clone', () => {
+  const du = dayjs().utcOffset(9)
+  const duClone = du.clone()
+  expect(du.valueOf()).toBe(duClone.valueOf())
+  expect(du.format()).toBe(duClone.format())
+  expect(du.utcOffset()).toBe(duClone.utcOffset())
+})
+
+it('immutable', () => {
+  const d = dayjs()
+  const du = d.utcOffset(9)
+  expect(d.utcOffset()).not.toBe(du.utcOffset())
+  expect(d.format()).not.toBe(du.format())
+})
+
+it('utcOffset(0) enable utc mode', () => {
+  expect(dayjs().utcOffset(0).format()).toBe(moment().utcOffset(0).format())
+  expect(dayjs().utcOffset(0).isUTC()).toBeTruthy()
+})
+
+test('UTC mode', () => {
+  const d = dayjs.utc('2000-01-01T06:00:00Z')
+  expect(d.isUTC()).toBeTruthy()
+  expect(d.utcOffset(0).isUTC()).toBeTruthy()
+  expect(d.utcOffset(1).isUTC()).toBeFalsy()
+})
+
+test('change hours when changing the utc offset in UTC mode', () => {
+  const d = dayjs.utc('2000-01-01T06:31:00Z')
+  expect(d.hour()).toBe(6)
+  expect(d.utcOffset(0).hour()).toBe(6)
+  expect(d.utcOffset(-60).hour()).toBe(5)
+  expect(d.utcOffset(60).hour()).toBe(7)
+  expect(d.utcOffset(-30).format('HH:mm')).toBe('06:01')
+  expect(d.utcOffset(30).format('HH:mm')).toBe('07:01')
+  expect(d.utcOffset(-1380).format('HH:mm')).toBe('07:31')
+})

{"test/plugin/utc-utcOffset.test.js": {"run": "NONE", "test": "FAIL", "fix": "PASS"}}
{"test/timezone.test.js:Add Time days (DST)": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/get-set.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/utc.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/quarterOfYear.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/advancedFormat.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/weekYear.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/toArray.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/timezone.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/isSameOrBefore.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/timezone.test.js:Utc Offset": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/timezone.test.js:Diff (DST)": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/locale/keys.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/utils.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/relativeTime.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/parse.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/isMoment.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/toObject.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/localizedFormat.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/constructor.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/weekday.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/isSameOrAfter.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/weekOfYear.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/comparison.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/isLeapYear.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/query.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/localeData.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/calendar.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/declarations.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/locale.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/badMutable.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/customParseFormat.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/locale/ru.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/dayOfYear.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/minMax.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/isoWeeksInYear.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/timezone.test.js:UTC add day in DST": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/buddhistEra.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/manipulate.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}, "test/plugin/isBetween.test.js": {"run": "PASS", "test": "PASS", "fix": "PASS"}}
{"test/plugin/utc-utcOffset.test.js": {"run": "NONE", "test": "FAIL", "fix": "PASS"}}
{"passed_count": 40, "failed_count": 1, "skipped_count": 0, "passed_tests": ["test/timezone.test.js:Add Time days (DST)", "test/plugin.test.js", "test/get-set.test.js", "test/plugin/utc.test.js", "test/plugin/quarterOfYear.test.js", "test/plugin/advancedFormat.test.js", "test/plugin/weekYear.test.js", "test/plugin/toArray.test.js", "test/timezone.test.js", "test/plugin/isSameOrBefore.test.js", "test/timezone.test.js:Utc Offset", "test/timezone.test.js:Diff (DST)", "test/locale/keys.test.js", "test/utils.test.js", "test/plugin/relativeTime.test.js", "test/parse.test.js", "test/plugin/isMoment.test.js", "test/plugin/toObject.test.js", "test/plugin/localizedFormat.test.js", "test/constructor.test.js", "test/plugin/weekday.test.js", "test/plugin/isSameOrAfter.test.js", "test/plugin/weekOfYear.test.js", "test/comparison.test.js", "test/plugin/isLeapYear.test.js", "test/query.test.js", "test/plugin/localeData.test.js", "test/plugin/calendar.test.js", "test/plugin/declarations.test.js", "test/locale.test.js", "test/plugin/badMutable.test.js", "test/plugin/customParseFormat.test.js", "test/locale/ru.test.js", "test/plugin/dayOfYear.test.js", "test/plugin/minMax.test.js", "test/plugin/isoWeeksInYear.test.js", "test/timezone.test.js:UTC add day in DST", "test/plugin/buddhistEra.test.js", "test/manipulate.test.js", "test/plugin/isBetween.test.js"], "failed_tests": ["test/display.test.js"], "skipped_tests": []}
{"passed_count": 40, "failed_count": 2, "skipped_count": 0, "passed_tests": ["test/timezone.test.js:Add Time days (DST)", "test/plugin.test.js", "test/get-set.test.js", "test/plugin/utc.test.js", "test/plugin/quarterOfYear.test.js", "test/plugin/advancedFormat.test.js", "test/plugin/toArray.test.js", "test/plugin/weekYear.test.js", "test/timezone.test.js", "test/timezone.test.js:Utc Offset", "test/plugin/isSameOrBefore.test.js", "test/timezone.test.js:Diff (DST)", "test/locale/keys.test.js", "test/utils.test.js", "test/plugin/relativeTime.test.js", "test/parse.test.js", "test/plugin/isMoment.test.js", "test/plugin/toObject.test.js", "test/plugin/localizedFormat.test.js", "test/constructor.test.js", "test/plugin/weekday.test.js", "test/plugin/isSameOrAfter.test.js", "test/plugin/weekOfYear.test.js", "test/comparison.test.js", "test/plugin/isLeapYear.test.js", "test/query.test.js", "test/plugin/localeData.test.js", "test/plugin/calendar.test.js", "test/plugin/declarations.test.js", "test/plugin/badMutable.test.js", "test/locale.test.js", "test/plugin/customParseFormat.test.js", "test/locale/ru.test.js", "test/plugin/minMax.test.js", "test/plugin/dayOfYear.test.js", "test/plugin/isoWeeksInYear.test.js", "test/timezone.test.js:UTC add day in DST", "test/manipulate.test.js", "test/plugin/buddhistEra.test.js", "test/plugin/isBetween.test.js"], "failed_tests": ["test/plugin/utc-utcOffset.test.js", "test/display.test.js"], "skipped_tests": []}
{"passed_count": 41, "failed_count": 1, "skipped_count": 0, "passed_tests": ["test/timezone.test.js:Add Time days (DST)", "test/plugin.test.js", "test/get-set.test.js", "test/plugin/utc.test.js", "test/plugin/quarterOfYear.test.js", "test/plugin/advancedFormat.test.js", "test/plugin/weekYear.test.js", "test/plugin/toArray.test.js", "test/timezone.test.js", "test/timezone.test.js:Utc Offset", "test/plugin/isSameOrBefore.test.js", "test/timezone.test.js:Diff (DST)", "test/plugin/utc-utcOffset.test.js", "test/locale/keys.test.js", "test/utils.test.js", "test/plugin/relativeTime.test.js", "test/parse.test.js", "test/plugin/isMoment.test.js", "test/plugin/toObject.test.js", "test/plugin/localizedFormat.test.js", "test/constructor.test.js", "test/plugin/weekday.test.js", "test/plugin/isSameOrAfter.test.js", "test/plugin/weekOfYear.test.js", "test/plugin/isLeapYear.test.js", "test/comparison.test.js", "test/query.test.js", "test/plugin/localeData.test.js", "test/plugin/calendar.test.js", "test/plugin/declarations.test.js", "test/plugin/badMutable.test.js", "test/locale.test.js", "test/plugin/customParseFormat.test.js", "test/locale/ru.test.js", "test/plugin/dayOfYear.test.js", "test/plugin/minMax.test.js", "test/plugin/isoWeeksInYear.test.js", "test/timezone.test.js:UTC add day in DST", "test/plugin/buddhistEra.test.js", "test/manipulate.test.js", "test/plugin/isBetween.test.js"], "failed_tests": ["test/display.test.js"], "skipped_tests": []}