Validating Date Parsing Implementation in dayjs
This test suite validates the date parsing functionality in the dayjs library, comparing its behavior with moment.js. It covers various date formats, time zones, and edge cases to ensure consistent date handling across different input types.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
iamkun/dayjs
test/parse.test.js
import moment from 'moment'
import MockDate from 'mockdate'
import dayjs from '../src'
import { REGEX_PARSE } from '../src/constant'
beforeEach(() => {
MockDate.set(new Date())
})
afterEach(() => {
MockDate.reset()
})
describe('Parse', () => {
it('Now', () => {
expect(dayjs().valueOf()).toBe(moment().valueOf())
})
it('moment-js like formatted dates', () => {
global.console.warn = jest.genMockFunction()// moment.js '2018-4-1 1:1:1:22' will throw warn
let d = '20130108'
expect(dayjs(d).valueOf()).toBe(moment(d).valueOf())
d = '2018-04-24'
expect(dayjs(d).valueOf()).toBe(moment(d).valueOf())
d = '2018-04-24 11:12'
expect(dayjs(d).format()).toBe(moment(d).format()) // not recommend
d = '2018-05-02 11:12:13'
expect(dayjs(d).valueOf()).toBe(moment(d).valueOf())
d = '2018-05-02 11:12:13.998'
expect(dayjs(d).valueOf()).toBe(moment(d).valueOf())
d = '2018-4-1'
expect(dayjs(d).valueOf()).toBe(moment(d).valueOf()) // not recommend
d = '2018-4-1 11:12'
expect(dayjs(d).format()).toBe(moment(d).format()) // not recommend
d = '2018-4-1 1:1:1:223'
expect(dayjs(d).valueOf()).toBe(moment(d).valueOf()) // not recommend
d = '2018-01'
expect(dayjs(d).valueOf()).toBe(moment(d).valueOf()) // not recommend
d = '2018'
expect(dayjs(d).format()).toBe(moment(d).format()) // not recommend
d = '2018-05-02T11:12:13Z' // should go direct to new Date() rather our regex
expect(dayjs(d).format()).toBe(moment(d).format()) // not recommend
})
it('String ISO 8601 date, time and zone', () => {
const time = '2018-04-04T16:00:00.000Z'
expect(dayjs(time).valueOf()).toBe(moment(time).valueOf())
})
it('String RFC 2822, time and zone', () => {
const time = 'Mon, 11 Feb 2019 09:46:50 GMT+1'
const expected = '2019-02-11T08:46:50.000Z'
const d = dayjs(time)
expect(d.toISOString()).toEqual(expected)
expect(d.valueOf()).toBe(moment(time).valueOf())
})
it('String ECMAScript, time and zone', () => {
// should parse dates formatted in ECMA script format
// see https://www.ecma-international.org/ecma-262/9.0/index.html#sec-date.prototype.tostring
const time = 'Mon Feb 11 2019 11:01:37 GMT+0100 (Mitteleuropäische Normalzeit)'
const expected = '2019-02-11T10:01:37.000Z'
const d = dayjs(time)
expect(d.toISOString()).toEqual(expected)
expect(d.valueOf()).toBe(moment(time).valueOf())
})
it('rejects invalid values', () => {
expect(dayjs({}).isValid()).toBe(false)
expect(dayjs(() => '2018-01-01').isValid()).toBe(false)
expect(dayjs(Infinity).isValid()).toBe(false)
expect(dayjs(NaN).isValid()).toBe(false)
expect(dayjs([2018, 5, 1, 13, 52, 44]).isValid()).toBe(false) // Arrays with time part
})
it('parses Arrays with date part', () => {
const dateParts = [2018, 5, 1]
const expected = '2018-05-01T00:00:00.000Z'
const d = dayjs(dateParts)
const normalized = d.add(d.utcOffset(), 'minutes') // make test run in every timezone
expect(normalized.toISOString()).toEqual(expected)
})
it('parses unlimited millisecond', () => {
const date = '2019-03-25T06:41:00.999999999'
const ds = dayjs(date)
const ms = moment(date)
expect(ds.valueOf()).toEqual(ms.valueOf())
expect(ds.millisecond()).toEqual(ms.millisecond())
})
it('String Other, Undefined and Null and isValid', () => {
global.console.warn = jest.genMockFunction()// moment.js otherString will throw warn
expect(dayjs('otherString').toString().toLowerCase()).toBe(moment('otherString').toString().toLowerCase())
expect(dayjs(undefined).toDate()).toEqual(moment(undefined).toDate())
expect(dayjs().isValid()).toBe(true)
expect(dayjs(undefined).isValid()).toBe(true)
expect(dayjs('').isValid()).toBe(false)
expect(dayjs(null).isValid()).toBe(false)
expect(dayjs('otherString').isValid()).toBe(false)
expect(dayjs(null).toString().toLowerCase()).toBe(moment(null).toString().toLowerCase())
})
})
it('Unix Timestamp Number (milliseconds) 1523520536000', () => {
const timestamp = 1523520536000
expect(dayjs(timestamp).valueOf()).toBe(moment(timestamp).valueOf())
})
it('Unix Timestamp Number (seconds) 1318781876', () => {
const timestamp1 = 1318781876
const timestamp2 = 1318781876.721
expect(dayjs.unix(timestamp1).valueOf()).toBe(moment.unix(timestamp1).valueOf())
expect(dayjs.unix(timestamp2).valueOf()).toBe(moment.unix(timestamp2).valueOf())
})
it('String and Number 20180101', () => {
expect(dayjs(20180101).valueOf()).toBe(moment(20180101).valueOf())
expect(dayjs('20180101').valueOf()).toBe(moment('20180101').valueOf())
})
it('Number 0', () => {
expect(dayjs(0).valueOf()).toBe(moment(0).valueOf())
})
it('Clone not affect each other', () => {
const base = dayjs(20170101)
const year = base.year()
const another = base.set('year', year + 1)
expect(another.unix() - base.unix()).toBe(31536000)
})
it('Clone with same value', () => {
const base = dayjs()
const year = base.year()
const newBase = base.set('year', year + 1)
const another = newBase.clone()
expect(newBase.toString()).toBe(another.toString())
})
describe('REGEX_PARSE', () => {
it('2020/9/30', () => {
const date = '2020/9/30'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d.join('-')).toBe('2020/9/30-2020-9-30----')
})
it('2019-03-25T06:41:00.999999999', () => {
const date = '2019-03-25T06:41:00.999999999'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d.join('-')).toBe('2019-03-25T06:41:00.999999999-2019-03-25-06-41-00-999999999')
})
it('20210102T012345', () => {
const date = '20210102T012345'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d.join('-')).toBe('20210102T012345-2021-01-02-01-23-45-')
})
it('2021-01-02T01:23', () => {
const date = '2021-01-02T01:23'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d.join('-')).toBe('2021-01-02T01:23-2021-01-02-01-23--')
})
it('2021-01-02T01:23:45', () => {
const date = '2021-01-02T01:23:45'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d.join('-')).toBe('2021-01-02T01:23:45-2021-01-02-01-23-45-')
})
it('2020-12-31T18:00:00.000-0500 (no regex match)', () => {
const date = '2020-12-31T18:00:00.000-0500'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d).toBe(null)
})
// format used in timezone plugin utcString
it('2021-1-4 0:42:53:000', () => {
const date = '2021-1-4 0:42:53:000'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d.join('-')).toBe('2021-1-4 0:42:53:000-2021-1-4-0-42-53-000')
})
it('2020-12-31T18:00:00-05:00 (no regex match)', () => {
const date = '2020-12-31T18:00:00-05:00'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d).toBe(null)
})
it('2021-01-02T01:23:45-0500 (no regex match)', () => {
const date = '2021-01-02T01:23:45-0500'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d).toBe(null)
})
it('2021-01-02T01:23:45Z (no regex match)', () => {
const date = '2021-01-02T01:23:45Z'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d).toBe(null)
})
// dots should not be matched, and fallback to Date
it('2021.01.03', () => {
const date = '2021.01.03'
const d = date.match(REGEX_PARSE)
expect(dayjs(date).valueOf()).toBe(moment(date).valueOf())
expect(d).toBe(null)
})
})