Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: customParseFormat strict mode bug when parsing with timezones (#929) #1467

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
52 changes: 41 additions & 11 deletions src/plugin/customParseFormat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,12 @@ function makeParser(format) {

const parseFormattedInput = (input, format, utc) => {
try {
if (['x', 'X'].indexOf(format) > -1) return new Date((format === 'X' ? 1000 : 1) * input)
if (['x', 'X'].indexOf(format) > -1) {
return {
parsedDate: new Date((format === 'X' ? 1000 : 1) * input)
}
}

const parser = makeParser(format)
const {
year, month, day, hours, minutes, seconds, milliseconds, zone
Expand All @@ -192,14 +197,24 @@ const parseFormattedInput = (input, format, utc) => {
const s = seconds || 0
const ms = milliseconds || 0
if (zone) {
return new Date(Date.UTC(y, M, d, h, m, s, ms + (zone.offset * 60 * 1000)))
const parsedOffsetMilliseconds = zone.offset * 60 * 1000
return {
parsedOffsetMilliseconds,
parsedDate: new Date(Date.UTC(y, M, d, h, m, s, ms + parsedOffsetMilliseconds))
}
}
if (utc) {
return new Date(Date.UTC(y, M, d, h, m, s, ms))
return {
parsedDate: new Date(Date.UTC(y, M, d, h, m, s, ms))
}
}
return {
parsedDate: new Date(y, M, d, h, m, s, ms)
}
return new Date(y, M, d, h, m, s, ms)
} catch (e) {
return new Date('') // Invalid Date
return {
parsedDate: new Date('') // Invalid Date
}
}
}

Expand Down Expand Up @@ -229,14 +244,29 @@ export default (o, C, d) => {
if (!isStrictWithoutLocale && pl) {
locale = d.Ls[pl]
}
this.$d = parseFormattedInput(date, format, utc)
const { parsedDate, parsedOffsetMilliseconds } = parseFormattedInput(date, format, utc)
this.$d = parsedDate
this.init()
if (pl && pl !== true) this.$L = this.locale(pl).$L
// use != to treat
// input number 1410715640579 and format string '1410715640579' equal
// eslint-disable-next-line eqeqeq
if (isStrict && date != this.format(format)) {
this.$d = new Date('')
const currentOffset = this.utcOffset()
if (isStrict) {
const dateWithoutTimezone = date.replace(/[+-]\d\d:?\d\d$/, '')
const parsedDateWithoutTimezone = this.subtract(currentOffset, 'minute').subtract(parsedOffsetMilliseconds, 'millisecond').format(format.replace(/(Z|ZZ)$/, ''))

// use != to treat
// input number 1410715640579 and format string '1410715640579' equal
if (
(
parsedOffsetMilliseconds !== undefined
&& dateWithoutTimezone !== parsedDateWithoutTimezone
) || (
parsedOffsetMilliseconds === undefined
// eslint-disable-next-line eqeqeq
&& date != this.format(format)
)
) {
this.$d = new Date('')
}
}
// reset global locale to make parallel unit test
locale = {}
Expand Down
45 changes: 45 additions & 0 deletions test/plugin/customParseFormat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,51 @@ describe('Strict mode', () => {
expect(dayjs(input, format, 'zh-cn').isValid()).toBe(true)
expect(dayjs(input, format, 'zh-cn', true).isValid()).toBe(false)
})
it('with timezone (normal format) and with milliseconds', () => {
const format = 'YYYY-MM-DDTHH:mm:ss.SSSZ'

const firstInput = '2021-04-01T12:47:29.000+02:00'
expect(dayjs(firstInput, format).isValid()).toBe(true)
expect(dayjs(firstInput, format, true).isValid()).toBe(true)

const secondInput = '2021-03-01T12:47:29.000+02:00'
expect(dayjs(secondInput, format).isValid()).toBe(true)
expect(dayjs(secondInput, format, true).isValid()).toBe(true)

const thirdInput = '2021-03-01T12:47:29.000'
expect(dayjs(thirdInput, format).isValid()).toBe(false)
expect(dayjs(thirdInput, format, true).isValid()).toBe(false)
})
it('with timezone (compact format) and with milliseconds', () => {
const format = 'YYYY-MM-DDTHH:mm:ss.SSSZZ'

const firstInput = '2021-04-01T12:47:29.000+0200'
expect(dayjs(firstInput, format).isValid()).toBe(true)
expect(dayjs(firstInput, format, true).isValid()).toBe(true)

const secondInput = '2021-03-01T12:47:29.000+0200'
expect(dayjs(secondInput, format).isValid()).toBe(true)
expect(dayjs(secondInput, format, true).isValid()).toBe(true)

const thirdInput = '2021-03-01T12:47:29.000'
expect(dayjs(thirdInput, format).isValid()).toBe(false)
expect(dayjs(thirdInput, format, true).isValid()).toBe(false)
})
it('without timezone and with milliseconds', () => {
const format = 'YYYY-MM-DDTHH:mm:ss.SSS'

const firstInput = '2021-04-01T12:47:29.000'
expect(dayjs(firstInput, format).isValid()).toBe(true)
expect(dayjs(firstInput, format, true).isValid()).toBe(true)

const secondInput = '2021-03-01T12:47:29.000'
expect(dayjs(secondInput, format).isValid()).toBe(true)
expect(dayjs(secondInput, format, true).isValid()).toBe(true)

const thirdInput = '2021-03-01T12:47:29.000+0200'
expect(dayjs(thirdInput, format).isValid()).toBe(true)
expect(dayjs(thirdInput, format, true).isValid()).toBe(false)
})
})

describe('Array format support', () => {
Expand Down