Skip to content

Commit

Permalink
fix: Add UTC mode with UTC plugin (#517)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamkun authored Mar 6, 2019
1 parent 5fc05a6 commit caf335c
Show file tree
Hide file tree
Showing 11 changed files with 394 additions and 73 deletions.
90 changes: 49 additions & 41 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,25 @@ 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 })
const wrapper = (date, instance) => dayjs(date, { locale: instance.$L, utc: instance.$u })

const Utils = U // for plugin use
Utils.parseLocale = parseLocale
Utils.isDayjs = isDayjs
Utils.wrapper = wrapper
Utils.l = parseLocale
Utils.i = isDayjs
Utils.w = wrapper

const parseDate = (date) => {
const parseDate = (cfg) => {
const { date, utc } = cfg
if (date === null) return new Date(NaN) // null is invalid
if (Utils.isUndefined(date)) return new Date() // today
if (date instanceof Date) return date
if (Utils.u(date)) return new Date() // today
if (date instanceof Date) return new Date(date)
if (typeof date === 'string' && !/Z$/i.test(date)) {
const d = date.match(C.REGEX_PARSE)
if (d) {
if (utc) {
return new Date(Date.UTC(d[1], d[2] - 1, d[3]
|| 1, d[4] || 0, d[5] || 0, d[6] || 0, d[7] || 0))
}
return new Date(d[1], d[2] - 1, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, d[7] || 0)
}
}
Expand All @@ -66,7 +71,7 @@ class Dayjs {
}

parse(cfg) {
this.$d = parseDate(cfg.date)
this.$d = parseDate(cfg)
this.init()
}

Expand Down Expand Up @@ -146,21 +151,23 @@ class Dayjs {
}

startOf(units, startOf) { // startOf -> endOf
const isStartOf = !Utils.isUndefined(startOf) ? startOf : true
const unit = Utils.prettyUnit(units)
const isStartOf = !Utils.u(startOf) ? startOf : true
const unit = Utils.p(units)
const instanceFactory = (d, m) => {
const ins = Utils.wrapper(new Date(this.$y, m, d), this)
const ins = Utils.w(this.$u ?
Date.UTC(this.$y, m, d) : new Date(this.$y, m, d), this)
return isStartOf ? ins : ins.endOf(C.D)
}
const instanceFactorySet = (method, slice) => {
const argumentStart = [0, 0, 0, 0]
const argumentEnd = [23, 59, 59, 999]
return Utils.wrapper(this.toDate()[method].apply( // eslint-disable-line prefer-spread
return Utils.w(this.toDate()[method].apply( // eslint-disable-line prefer-spread
this.toDate(),
(isStartOf ? argumentStart : argumentEnd).slice(slice)
), this)
}
const { $W, $M, $D } = this
const utcPad = `set${this.$u ? 'UTC' : ''}`
switch (unit) {
case C.Y:
return isStartOf ? instanceFactory(1, 0) :
Expand All @@ -175,13 +182,13 @@ class Dayjs {
}
case C.D:
case C.DATE:
return instanceFactorySet('setHours', 0)
return instanceFactorySet(`${utcPad}Hours`, 0)
case C.H:
return instanceFactorySet('setMinutes', 1)
return instanceFactorySet(`${utcPad}Minutes`, 1)
case C.MIN:
return instanceFactorySet('setSeconds', 2)
return instanceFactorySet(`${utcPad}Seconds`, 2)
case C.S:
return instanceFactorySet('setMilliseconds', 3)
return instanceFactorySet(`${utcPad}Milliseconds`, 3)
default:
return this.clone()
}
Expand All @@ -192,16 +199,17 @@ class Dayjs {
}

$set(units, int) { // private set
const unit = Utils.prettyUnit(units)
const unit = Utils.p(units)
const utcPad = `set${this.$u ? 'UTC' : ''}`
const name = {
[C.D]: 'setDate',
[C.DATE]: 'setDate',
[C.M]: 'setMonth',
[C.Y]: 'setFullYear',
[C.H]: 'setHours',
[C.MIN]: 'setMinutes',
[C.S]: 'setSeconds',
[C.MS]: 'setMilliseconds'
[C.D]: `${utcPad}Date`,
[C.DATE]: `${utcPad}Date`,
[C.M]: `${utcPad}Month`,
[C.Y]: `${utcPad}FullYear`,
[C.H]: `${utcPad}Hours`,
[C.MIN]: `${utcPad}Minutes`,
[C.S]: `${utcPad}Seconds`,
[C.MS]: `${utcPad}Milliseconds`
}[unit]
const arg = unit === C.D ? this.$D + (int - this.$W) : int

Expand All @@ -217,15 +225,15 @@ class Dayjs {

add(number, units) {
number = Number(number) // eslint-disable-line no-param-reassign
const unit = Utils.prettyUnit(units)
const unit = Utils.p(units)
const instanceFactory = (u, n) => {
const date = this.set(C.DATE, 1).set(u, n + number)
return date.set(C.DATE, Math.min(this.$D, date.daysInMonth()))
}
const instanceFactorySet = (n) => {
const date = new Date(this.$d)
date.setDate(date.getDate() + (n * number))
return Utils.wrapper(date, this)
return Utils.w(date, this)
}
if (unit === C.M) {
return instanceFactory(C.M, this.$M)
Expand All @@ -246,7 +254,7 @@ class Dayjs {
}[unit] || 1 // ms

const nextTimeStamp = this.valueOf() + (number * step)
return Utils.wrapper(nextTimeStamp, this)
return Utils.w(nextTimeStamp, this)
}

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

const str = formatStr || C.FORMAT_DEFAULT
const zoneStr = Utils.padZoneStr(this.$d.getTimezoneOffset())
const zoneStr = Utils.z(this)
const locale = this.$locale()
const {
weekdays, months
Expand All @@ -266,34 +274,34 @@ class Dayjs {
(arr && arr[index]) || full[index].substr(0, length)
)
const get$H = num => (
Utils.padStart(this.$H % 12 || 12, num, '0')
Utils.s(this.$H % 12 || 12, num, '0')
)

const matches = {
YY: String(this.$y).slice(-2),
YYYY: String(this.$y),
M: String(this.$M + 1),
MM: Utils.padStart(this.$M + 1, 2, '0'),
MM: Utils.s(this.$M + 1, 2, '0'),
MMM: getShort(locale.monthsShort, this.$M, months, 3),
MMMM: months[this.$M],
D: String(this.$D),
DD: Utils.padStart(this.$D, 2, '0'),
DD: Utils.s(this.$D, 2, '0'),
d: String(this.$W),
dd: getShort(locale.weekdaysMin, this.$W, weekdays, 2),
ddd: getShort(locale.weekdaysShort, this.$W, weekdays, 3),
dddd: weekdays[this.$W],
H: String(this.$H),
HH: Utils.padStart(this.$H, 2, '0'),
HH: Utils.s(this.$H, 2, '0'),
h: get$H(1),
hh: get$H(2),
a: this.$H < 12 ? 'am' : 'pm',
A: this.$H < 12 ? 'AM' : 'PM',
m: String(this.$m),
mm: Utils.padStart(this.$m, 2, '0'),
mm: Utils.s(this.$m, 2, '0'),
s: String(this.$s),
ss: Utils.padStart(this.$s, 2, '0'),
SSS: Utils.padStart(this.$ms, 3, '0'),
Z: zoneStr
ss: Utils.s(this.$s, 2, '0'),
SSS: Utils.s(this.$ms, 3, '0'),
Z: zoneStr // 'ZZ' logic below
}

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

diff(input, units, float) {
const unit = Utils.prettyUnit(units)
const unit = Utils.p(units)
const that = dayjs(input)
const zoneDelta = (that.utcOffset() - this.utcOffset()) * C.MILLISECONDS_A_MINUTE
const diff = this - that
let result = Utils.monthDiff(this, that)
let result = Utils.m(this, that)

result = {
[C.Y]: result / 12,
Expand All @@ -326,7 +334,7 @@ class Dayjs {
[C.S]: diff / C.MILLISECONDS_A_SECOND
}[unit] || diff // milliseconds

return float ? result : Utils.absFloor(result)
return float ? result : Utils.a(result)
}

daysInMonth() {
Expand All @@ -344,7 +352,7 @@ class Dayjs {
}

clone() {
return Utils.wrapper(this.toDate(), this)
return Utils.w(this.toDate(), this)
}

toDate() {
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/advancedFormat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default (o, c, d) => { // locale needed later
}
case 'k':
case 'kk':
return utils.padStart(String(this.$H === 0 ? 24 : this.$H), match === 'k' ? 1 : 2, '0')
return utils.s(String(this.$H === 0 ? 24 : this.$H), match === 'k' ? 1 : 2, '0')
case 'X':
return Math.floor(this.$d.getTime() / 1000)
default: // 'x'
Expand Down
3 changes: 1 addition & 2 deletions src/plugin/buddhistEra/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ export default (o, c) => { // locale needed later
// extend en locale here
proto.format = function (formatStr) {
const yearBias = 543
const { padStart } = this.$utils()
const str = formatStr || FORMAT_DEFAULT
const result = str.replace(/BBBB|BB/g, (match) => {
const year = String(this.$y + yearBias)
const args = match === 'BB' ? [year.slice(-2), 2] : [year, 4]
return padStart(...args, '0')
return this.$utils().s(...args, '0')
})
return oldFormat.bind(this)(result)
}
Expand Down
27 changes: 20 additions & 7 deletions src/plugin/customParseFormat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ function makeParser(format) {
}
}

const parseFormattedInput = (input, format) => {
const parseFormattedInput = (input, format, utc) => {
try {
const parser = makeParser(format)
const {
Expand All @@ -149,10 +149,17 @@ const parseFormattedInput = (input, format) => {
) + (zone.offset * 60 * 1000))
}
const now = new Date()
return new Date(
year || now.getFullYear(), month > 0 ? month - 1 : now.getMonth(), day || now.getDate(),
hours || 0, minutes || 0, seconds || 0, milliseconds || 0
)
const y = year || now.getFullYear()
const M = month > 0 ? month - 1 : now.getMonth()
const d = day || now.getDate()
const h = hours || 0
const m = minutes || 0
const s = seconds || 0
const ms = milliseconds || 0
if (utc) {
return new Date(Date.UTC(y, M, d, h, m, s, ms))
}
return new Date(y, M, d, h, m, s, ms)
} catch (e) {
return new Date('') // Invalid Date
}
Expand All @@ -163,10 +170,16 @@ export default (o, C, d) => {
const proto = C.prototype
const oldParse = proto.parse
proto.parse = function (cfg) {
const { date: input, format, pl } = cfg
const {
date,
format,
pl,
utc
} = cfg
this.$u = utc
if (format) {
locale = pl ? d.Ls[pl] : this.$locale()
this.$d = parseFormattedInput(input, format)
this.$d = parseFormattedInput(date, format, utc)
this.init(cfg)
} else {
oldParse.call(this, cfg)
Expand Down
59 changes: 59 additions & 0 deletions src/plugin/utc/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export default (option, Dayjs, dayjs) => {
const proto = Dayjs.prototype
dayjs.utc = function (date, format) {
const cfg = { date, utc: true, format }
return new Dayjs(cfg) // eslint-disable-line no-use-before-define
}

proto.utc = function () {
return dayjs(this.toDate(), { locale: this.$L, utc: true })
}

proto.local = function () {
return dayjs(this.toDate(), { locale: this.$L, utc: false })
}

const oldParse = proto.parse
proto.parse = function (cfg) {
if (cfg.utc) {
this.$u = true
}
oldParse.call(this, cfg)
}

const oldInit = proto.init
proto.init = function () {
if (this.$u) {
const { $d } = this
this.$y = $d.getUTCFullYear()
this.$M = $d.getUTCMonth()
this.$D = $d.getUTCDate()
this.$W = $d.getUTCDay()
this.$H = $d.getUTCHours()
this.$m = $d.getUTCMinutes()
this.$s = $d.getUTCSeconds()
this.$ms = $d.getUTCMilliseconds()
} else {
oldInit.call(this)
}
}

const oldUtcOffset = proto.utcOffset
proto.utcOffset = function () {
if (this.$u) {
return 0
}
return oldUtcOffset.call(this)
}

const oldFormat = proto.format
const UTC_FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ss[Z]'
proto.format = function (formatStr) {
const str = formatStr || (this.$u ? UTC_FORMAT_DEFAULT : '')
return oldFormat.call(this, str)
}

proto.isUTC = function () {
return !!this.$u
}
}
19 changes: 10 additions & 9 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const padStart = (string, length, pad) => {
return `${Array((length + 1) - s.length).join(pad)}${string}`
}

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

export default {
padStart,
padZoneStr,
monthDiff,
absFloor,
prettyUnit,
isUndefined
s: padStart,
z: padZoneStr,
m: monthDiff,
a: absFloor,
p: prettyUnit,
u: isUndefined
}
Loading

0 comments on commit caf335c

Please sign in to comment.