Skip to content

Commit caf335c

Browse files
authored
fix: Add UTC mode with UTC plugin (#517)
1 parent 5fc05a6 commit caf335c

File tree

11 files changed

+394
-73
lines changed

11 files changed

+394
-73
lines changed

src/index.js

+49-41
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,25 @@ const dayjs = (date, c, pl) => {
3838
return new Dayjs(cfg) // eslint-disable-line no-use-before-define
3939
}
4040

41-
const wrapper = (date, instance) => dayjs(date, { locale: instance.$L })
41+
const wrapper = (date, instance) => dayjs(date, { locale: instance.$L, utc: instance.$u })
4242

4343
const Utils = U // for plugin use
44-
Utils.parseLocale = parseLocale
45-
Utils.isDayjs = isDayjs
46-
Utils.wrapper = wrapper
44+
Utils.l = parseLocale
45+
Utils.i = isDayjs
46+
Utils.w = wrapper
4747

48-
const parseDate = (date) => {
48+
const parseDate = (cfg) => {
49+
const { date, utc } = cfg
4950
if (date === null) return new Date(NaN) // null is invalid
50-
if (Utils.isUndefined(date)) return new Date() // today
51-
if (date instanceof Date) return date
51+
if (Utils.u(date)) return new Date() // today
52+
if (date instanceof Date) return new Date(date)
5253
if (typeof date === 'string' && !/Z$/i.test(date)) {
5354
const d = date.match(C.REGEX_PARSE)
5455
if (d) {
56+
if (utc) {
57+
return new Date(Date.UTC(d[1], d[2] - 1, d[3]
58+
|| 1, d[4] || 0, d[5] || 0, d[6] || 0, d[7] || 0))
59+
}
5560
return new Date(d[1], d[2] - 1, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, d[7] || 0)
5661
}
5762
}
@@ -66,7 +71,7 @@ class Dayjs {
6671
}
6772

6873
parse(cfg) {
69-
this.$d = parseDate(cfg.date)
74+
this.$d = parseDate(cfg)
7075
this.init()
7176
}
7277

@@ -146,21 +151,23 @@ class Dayjs {
146151
}
147152

148153
startOf(units, startOf) { // startOf -> endOf
149-
const isStartOf = !Utils.isUndefined(startOf) ? startOf : true
150-
const unit = Utils.prettyUnit(units)
154+
const isStartOf = !Utils.u(startOf) ? startOf : true
155+
const unit = Utils.p(units)
151156
const instanceFactory = (d, m) => {
152-
const ins = Utils.wrapper(new Date(this.$y, m, d), this)
157+
const ins = Utils.w(this.$u ?
158+
Date.UTC(this.$y, m, d) : new Date(this.$y, m, d), this)
153159
return isStartOf ? ins : ins.endOf(C.D)
154160
}
155161
const instanceFactorySet = (method, slice) => {
156162
const argumentStart = [0, 0, 0, 0]
157163
const argumentEnd = [23, 59, 59, 999]
158-
return Utils.wrapper(this.toDate()[method].apply( // eslint-disable-line prefer-spread
164+
return Utils.w(this.toDate()[method].apply( // eslint-disable-line prefer-spread
159165
this.toDate(),
160166
(isStartOf ? argumentStart : argumentEnd).slice(slice)
161167
), this)
162168
}
163169
const { $W, $M, $D } = this
170+
const utcPad = `set${this.$u ? 'UTC' : ''}`
164171
switch (unit) {
165172
case C.Y:
166173
return isStartOf ? instanceFactory(1, 0) :
@@ -175,13 +182,13 @@ class Dayjs {
175182
}
176183
case C.D:
177184
case C.DATE:
178-
return instanceFactorySet('setHours', 0)
185+
return instanceFactorySet(`${utcPad}Hours`, 0)
179186
case C.H:
180-
return instanceFactorySet('setMinutes', 1)
187+
return instanceFactorySet(`${utcPad}Minutes`, 1)
181188
case C.MIN:
182-
return instanceFactorySet('setSeconds', 2)
189+
return instanceFactorySet(`${utcPad}Seconds`, 2)
183190
case C.S:
184-
return instanceFactorySet('setMilliseconds', 3)
191+
return instanceFactorySet(`${utcPad}Milliseconds`, 3)
185192
default:
186193
return this.clone()
187194
}
@@ -192,16 +199,17 @@ class Dayjs {
192199
}
193200

194201
$set(units, int) { // private set
195-
const unit = Utils.prettyUnit(units)
202+
const unit = Utils.p(units)
203+
const utcPad = `set${this.$u ? 'UTC' : ''}`
196204
const name = {
197-
[C.D]: 'setDate',
198-
[C.DATE]: 'setDate',
199-
[C.M]: 'setMonth',
200-
[C.Y]: 'setFullYear',
201-
[C.H]: 'setHours',
202-
[C.MIN]: 'setMinutes',
203-
[C.S]: 'setSeconds',
204-
[C.MS]: 'setMilliseconds'
205+
[C.D]: `${utcPad}Date`,
206+
[C.DATE]: `${utcPad}Date`,
207+
[C.M]: `${utcPad}Month`,
208+
[C.Y]: `${utcPad}FullYear`,
209+
[C.H]: `${utcPad}Hours`,
210+
[C.MIN]: `${utcPad}Minutes`,
211+
[C.S]: `${utcPad}Seconds`,
212+
[C.MS]: `${utcPad}Milliseconds`
205213
}[unit]
206214
const arg = unit === C.D ? this.$D + (int - this.$W) : int
207215

@@ -217,15 +225,15 @@ class Dayjs {
217225

218226
add(number, units) {
219227
number = Number(number) // eslint-disable-line no-param-reassign
220-
const unit = Utils.prettyUnit(units)
228+
const unit = Utils.p(units)
221229
const instanceFactory = (u, n) => {
222230
const date = this.set(C.DATE, 1).set(u, n + number)
223231
return date.set(C.DATE, Math.min(this.$D, date.daysInMonth()))
224232
}
225233
const instanceFactorySet = (n) => {
226234
const date = new Date(this.$d)
227235
date.setDate(date.getDate() + (n * number))
228-
return Utils.wrapper(date, this)
236+
return Utils.w(date, this)
229237
}
230238
if (unit === C.M) {
231239
return instanceFactory(C.M, this.$M)
@@ -246,7 +254,7 @@ class Dayjs {
246254
}[unit] || 1 // ms
247255

248256
const nextTimeStamp = this.valueOf() + (number * step)
249-
return Utils.wrapper(nextTimeStamp, this)
257+
return Utils.w(nextTimeStamp, this)
250258
}
251259

252260
subtract(number, string) {
@@ -257,7 +265,7 @@ class Dayjs {
257265
if (!this.isValid()) return C.INVALID_DATE_STRING
258266

259267
const str = formatStr || C.FORMAT_DEFAULT
260-
const zoneStr = Utils.padZoneStr(this.$d.getTimezoneOffset())
268+
const zoneStr = Utils.z(this)
261269
const locale = this.$locale()
262270
const {
263271
weekdays, months
@@ -266,34 +274,34 @@ class Dayjs {
266274
(arr && arr[index]) || full[index].substr(0, length)
267275
)
268276
const get$H = num => (
269-
Utils.padStart(this.$H % 12 || 12, num, '0')
277+
Utils.s(this.$H % 12 || 12, num, '0')
270278
)
271279

272280
const matches = {
273281
YY: String(this.$y).slice(-2),
274282
YYYY: String(this.$y),
275283
M: String(this.$M + 1),
276-
MM: Utils.padStart(this.$M + 1, 2, '0'),
284+
MM: Utils.s(this.$M + 1, 2, '0'),
277285
MMM: getShort(locale.monthsShort, this.$M, months, 3),
278286
MMMM: months[this.$M],
279287
D: String(this.$D),
280-
DD: Utils.padStart(this.$D, 2, '0'),
288+
DD: Utils.s(this.$D, 2, '0'),
281289
d: String(this.$W),
282290
dd: getShort(locale.weekdaysMin, this.$W, weekdays, 2),
283291
ddd: getShort(locale.weekdaysShort, this.$W, weekdays, 3),
284292
dddd: weekdays[this.$W],
285293
H: String(this.$H),
286-
HH: Utils.padStart(this.$H, 2, '0'),
294+
HH: Utils.s(this.$H, 2, '0'),
287295
h: get$H(1),
288296
hh: get$H(2),
289297
a: this.$H < 12 ? 'am' : 'pm',
290298
A: this.$H < 12 ? 'AM' : 'PM',
291299
m: String(this.$m),
292-
mm: Utils.padStart(this.$m, 2, '0'),
300+
mm: Utils.s(this.$m, 2, '0'),
293301
s: String(this.$s),
294-
ss: Utils.padStart(this.$s, 2, '0'),
295-
SSS: Utils.padStart(this.$ms, 3, '0'),
296-
Z: zoneStr
302+
ss: Utils.s(this.$s, 2, '0'),
303+
SSS: Utils.s(this.$ms, 3, '0'),
304+
Z: zoneStr // 'ZZ' logic below
297305
}
298306

299307
return str.replace(C.REGEX_FORMAT, (match) => {
@@ -309,11 +317,11 @@ class Dayjs {
309317
}
310318

311319
diff(input, units, float) {
312-
const unit = Utils.prettyUnit(units)
320+
const unit = Utils.p(units)
313321
const that = dayjs(input)
314322
const zoneDelta = (that.utcOffset() - this.utcOffset()) * C.MILLISECONDS_A_MINUTE
315323
const diff = this - that
316-
let result = Utils.monthDiff(this, that)
324+
let result = Utils.m(this, that)
317325

318326
result = {
319327
[C.Y]: result / 12,
@@ -326,7 +334,7 @@ class Dayjs {
326334
[C.S]: diff / C.MILLISECONDS_A_SECOND
327335
}[unit] || diff // milliseconds
328336

329-
return float ? result : Utils.absFloor(result)
337+
return float ? result : Utils.a(result)
330338
}
331339

332340
daysInMonth() {
@@ -344,7 +352,7 @@ class Dayjs {
344352
}
345353

346354
clone() {
347-
return Utils.wrapper(this.toDate(), this)
355+
return Utils.w(this.toDate(), this)
348356
}
349357

350358
toDate() {

src/plugin/advancedFormat/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default (o, c, d) => { // locale needed later
2222
}
2323
case 'k':
2424
case 'kk':
25-
return utils.padStart(String(this.$H === 0 ? 24 : this.$H), match === 'k' ? 1 : 2, '0')
25+
return utils.s(String(this.$H === 0 ? 24 : this.$H), match === 'k' ? 1 : 2, '0')
2626
case 'X':
2727
return Math.floor(this.$d.getTime() / 1000)
2828
default: // 'x'

src/plugin/buddhistEra/index.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ export default (o, c) => { // locale needed later
66
// extend en locale here
77
proto.format = function (formatStr) {
88
const yearBias = 543
9-
const { padStart } = this.$utils()
109
const str = formatStr || FORMAT_DEFAULT
1110
const result = str.replace(/BBBB|BB/g, (match) => {
1211
const year = String(this.$y + yearBias)
1312
const args = match === 'BB' ? [year.slice(-2), 2] : [year, 4]
14-
return padStart(...args, '0')
13+
return this.$utils().s(...args, '0')
1514
})
1615
return oldFormat.bind(this)(result)
1716
}

src/plugin/customParseFormat/index.js

+20-7
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ function makeParser(format) {
135135
}
136136
}
137137

138-
const parseFormattedInput = (input, format) => {
138+
const parseFormattedInput = (input, format, utc) => {
139139
try {
140140
const parser = makeParser(format)
141141
const {
@@ -149,10 +149,17 @@ const parseFormattedInput = (input, format) => {
149149
) + (zone.offset * 60 * 1000))
150150
}
151151
const now = new Date()
152-
return new Date(
153-
year || now.getFullYear(), month > 0 ? month - 1 : now.getMonth(), day || now.getDate(),
154-
hours || 0, minutes || 0, seconds || 0, milliseconds || 0
155-
)
152+
const y = year || now.getFullYear()
153+
const M = month > 0 ? month - 1 : now.getMonth()
154+
const d = day || now.getDate()
155+
const h = hours || 0
156+
const m = minutes || 0
157+
const s = seconds || 0
158+
const ms = milliseconds || 0
159+
if (utc) {
160+
return new Date(Date.UTC(y, M, d, h, m, s, ms))
161+
}
162+
return new Date(y, M, d, h, m, s, ms)
156163
} catch (e) {
157164
return new Date('') // Invalid Date
158165
}
@@ -163,10 +170,16 @@ export default (o, C, d) => {
163170
const proto = C.prototype
164171
const oldParse = proto.parse
165172
proto.parse = function (cfg) {
166-
const { date: input, format, pl } = cfg
173+
const {
174+
date,
175+
format,
176+
pl,
177+
utc
178+
} = cfg
179+
this.$u = utc
167180
if (format) {
168181
locale = pl ? d.Ls[pl] : this.$locale()
169-
this.$d = parseFormattedInput(input, format)
182+
this.$d = parseFormattedInput(date, format, utc)
170183
this.init(cfg)
171184
} else {
172185
oldParse.call(this, cfg)

src/plugin/utc/index.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
export default (option, Dayjs, dayjs) => {
2+
const proto = Dayjs.prototype
3+
dayjs.utc = function (date, format) {
4+
const cfg = { date, utc: true, format }
5+
return new Dayjs(cfg) // eslint-disable-line no-use-before-define
6+
}
7+
8+
proto.utc = function () {
9+
return dayjs(this.toDate(), { locale: this.$L, utc: true })
10+
}
11+
12+
proto.local = function () {
13+
return dayjs(this.toDate(), { locale: this.$L, utc: false })
14+
}
15+
16+
const oldParse = proto.parse
17+
proto.parse = function (cfg) {
18+
if (cfg.utc) {
19+
this.$u = true
20+
}
21+
oldParse.call(this, cfg)
22+
}
23+
24+
const oldInit = proto.init
25+
proto.init = function () {
26+
if (this.$u) {
27+
const { $d } = this
28+
this.$y = $d.getUTCFullYear()
29+
this.$M = $d.getUTCMonth()
30+
this.$D = $d.getUTCDate()
31+
this.$W = $d.getUTCDay()
32+
this.$H = $d.getUTCHours()
33+
this.$m = $d.getUTCMinutes()
34+
this.$s = $d.getUTCSeconds()
35+
this.$ms = $d.getUTCMilliseconds()
36+
} else {
37+
oldInit.call(this)
38+
}
39+
}
40+
41+
const oldUtcOffset = proto.utcOffset
42+
proto.utcOffset = function () {
43+
if (this.$u) {
44+
return 0
45+
}
46+
return oldUtcOffset.call(this)
47+
}
48+
49+
const oldFormat = proto.format
50+
const UTC_FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ss[Z]'
51+
proto.format = function (formatStr) {
52+
const str = formatStr || (this.$u ? UTC_FORMAT_DEFAULT : '')
53+
return oldFormat.call(this, str)
54+
}
55+
56+
proto.isUTC = function () {
57+
return !!this.$u
58+
}
59+
}

src/utils.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ const padStart = (string, length, pad) => {
66
return `${Array((length + 1) - s.length).join(pad)}${string}`
77
}
88

9-
const padZoneStr = (negMinuts) => {
9+
const padZoneStr = (instance) => {
10+
const negMinuts = -instance.utcOffset()
1011
const minutes = Math.abs(negMinuts)
1112
const hourOffset = Math.floor(minutes / 60)
1213
const minuteOffset = minutes % 60
@@ -16,9 +17,9 @@ const padZoneStr = (negMinuts) => {
1617
const monthDiff = (a, b) => {
1718
// function from moment.js in order to keep the same result
1819
const wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month())
19-
const anchor = a.clone().add(wholeMonthDiff, 'months')
20+
const anchor = a.clone().add(wholeMonthDiff, C.M)
2021
const c = b - anchor < 0
21-
const anchor2 = a.clone().add(wholeMonthDiff + (c ? -1 : 1), 'months')
22+
const anchor2 = a.clone().add(wholeMonthDiff + (c ? -1 : 1), C.M)
2223
return Number(-(wholeMonthDiff + ((b - anchor) / (c ? (anchor - anchor2) :
2324
(anchor2 - anchor)))) || 0)
2425
}
@@ -42,10 +43,10 @@ const prettyUnit = (u) => {
4243
const isUndefined = s => s === undefined
4344

4445
export default {
45-
padStart,
46-
padZoneStr,
47-
monthDiff,
48-
absFloor,
49-
prettyUnit,
50-
isUndefined
46+
s: padStart,
47+
z: padZoneStr,
48+
m: monthDiff,
49+
a: absFloor,
50+
p: prettyUnit,
51+
u: isUndefined
5152
}

0 commit comments

Comments
 (0)