Skip to content

Commit

Permalink
feat: make parseDate support more date formats (e.g. 3-digit years) [P…
Browse files Browse the repository at this point in the history
  • Loading branch information
pjanik committed Jul 22, 2024
1 parent 08b3e9b commit 430e488
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 9 deletions.
34 changes: 27 additions & 7 deletions v3/src/utilities/date-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,31 @@ describe('Date Parser tests - V2 compatibility', () => {
expect(parseDate('11/9/16', true)?.toISOString()).toBe(new Date(2016, 10, 9).toISOString())
expect(parseDate('11/09/16', true)?.toISOString()).toBe(new Date(2016, 10, 9).toISOString())
expect(parseDate('04/1/28', true)?.toISOString()).toBe(new Date(2028, 3, 1).toISOString())
// 3 digit year was not supported in V2
expect(parseDate('11/9/153', true)?.toISOString()).toBe(new Date(153, 10, 9).toISOString())
})
test('ISO dates', () => {
expect(parseDate('2016-01', true)?.toISOString()).toBe(new Date(2016, 0, 1).toISOString())
expect(parseDate('2016-02-02', true)?.toISOString()).toBe(new Date(2016, 1, 2).toISOString())
// 3 digit year was not supported in V2
expect(parseDate('153-11-9', true)?.toISOString()).toBe(new Date(153, 10, 9).toISOString())
})
test('day, month name, year dates', () => {
expect(parseDate('03 Mar 2016', true)?.toISOString()).toBe(new Date(2016, 2, 3).toISOString())
// 3 digit year was not supported in V2
expect(parseDate('09 Nov 153', true)?.toISOString()).toBe(new Date(153, 10, 9).toISOString())
})
test('traditional US dates', () => {
expect(parseDate('April 4, 2016', true)?.toISOString()).toBe(new Date(2016, 3, 4).toISOString())
expect(parseDate('Apr 5, 2016', true)?.toISOString()).toBe(new Date(2016, 3, 5).toISOString())
expect(parseDate('Monday, May 5, 2016', true)?.toISOString()).toBe(new Date(2016, 4, 5).toISOString())
// 3 digit year was not supported in V2
expect(parseDate('Monday, Nov 9 153', true)?.toISOString()).toBe(new Date(153, 10, 9).toISOString())
})
test('year.month.day dates', () => {
expect(parseDate('2016.6.6', true)?.toISOString()).toBe(new Date(2016, 5, 6).toISOString())
// 3 digit year was not supported in V2
expect(parseDate('153.11.9', true)?.toISOString()).toBe(new Date(153, 10, 9).toISOString())
})
test('unix dates', () => {
expect(parseDate('Thu Jul 11 09:12:47 PDT 2019', true)?.toISOString())
Expand Down Expand Up @@ -55,6 +65,12 @@ describe('Date Parser tests - V2 compatibility', () => {
.toBe(new Date(2016, 10, 9, 17, 18, 2).toISOString())
expect(parseDate('11/9/2016 17:18:02.123', true)?.toISOString())
.toBe(new Date(2016, 10, 9, 17, 18, 2, 123).toISOString())

// 3 digit year was not supported in V2
expect(parseDate('11/9/153 7:18', true)?.toISOString()).toBe(new Date(153, 10, 9, 7, 18).toISOString())
expect(parseDate('11/9/153 7:18:02', true)?.toISOString()).toBe(new Date(153, 10, 9, 7, 18, 2).toISOString())
expect(parseDate('11/9/153 7:18:02.123', true)?.toISOString())
.toBe(new Date(153, 10, 9, 7, 18, 2, 123).toISOString())
})
test('ISO 8601', () => {
expect(isDateString('2016-11-10')).toBe(true)
Expand All @@ -68,6 +84,10 @@ describe('Date Parser tests - V2 compatibility', () => {
expect(isDateString('2016-11-10T21:27:42.123Z')).toBe(true)
expect(isDateString('September 1, 2016')).toBe(true)
expect(isDateString('2016-11-10T21:27:42.12Z')).toBe(true)
// 3 digit year was not supported in V2
expect(isDateString('153', true)).toBe(true)
expect(isDateString('153-11-9', true)).toBe(true)
expect(isDateString('September 1, 153', true)).toBe(true)
})
test('invalid strings', () => {
expect(isDateString('')).toBe(false)
Expand Down Expand Up @@ -100,7 +120,7 @@ describe('isValidDateSpec', () => {
}
expect(isValidDateSpec(validDateSpec)).toEqual(validDateSpec)
})
test('returns null when year is NaN', () => {
test('returns null when year is NaN', () => {
const invalidDateSpec = {
year: NaN,
month: 7,
Expand All @@ -112,7 +132,7 @@ test('returns null when year is NaN', () => {
}
expect(isValidDateSpec(invalidDateSpec)).toBeFalsy()
})
test('returns null when month is out of range', () => {
test('returns null when month is out of range', () => {
const invalidDateSpec = {
year: 2023,
month: 13,
Expand All @@ -124,7 +144,7 @@ test('returns null when month is out of range', () => {
}
expect(isValidDateSpec(invalidDateSpec)).toBeFalsy()
})
test('returns null when day is out of range', () => {
test('returns null when day is out of range', () => {
const invalidDateSpec = {
year: 2023,
month: 7,
Expand All @@ -136,7 +156,7 @@ test('returns null when day is out of range', () => {
}
expect(isValidDateSpec(invalidDateSpec)).toBeFalsy()
})
test('returns null when hour is out of range', () => {
test('returns null when hour is out of range', () => {
const invalidDateSpec = {
year: 2023,
month: 7,
Expand All @@ -148,7 +168,7 @@ test('returns null when hour is out of range', () => {
}
expect(isValidDateSpec(invalidDateSpec)).toBeFalsy()
})
test('returns null when minute is out of range', () => {
test('returns null when minute is out of range', () => {
const invalidDateSpec = {
year: 2023,
month: 7,
Expand All @@ -160,7 +180,7 @@ test('returns null when minute is out of range', () => {
}
expect(isValidDateSpec(invalidDateSpec)).toBeFalsy()
})
test('returns null when second is out of range', () => {
test('returns null when second is out of range', () => {
const invalidDateSpec = {
year: 2023,
month: 7,
Expand All @@ -172,7 +192,7 @@ test('returns null when second is out of range', () => {
}
expect(isValidDateSpec(invalidDateSpec)).toBeFalsy()
})
test('returns null when subsecond is NaN', () => {
test('returns null when subsecond is NaN', () => {
const invalidDateSpec = {
year: 2023,
month: 7,
Expand Down
25 changes: 23 additions & 2 deletions v3/src/utilities/date-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export function isValidDateSpec(dateSpec: DateSpec) {
return isValid ? dateSpec : false
}

export function parseDate(iValue: any, iLoose?: boolean) {
export function parseDateV2Compatible(iValue: any, iLoose?: boolean) {
if (iValue == null) {
return null
}
Expand Down Expand Up @@ -267,6 +267,27 @@ export function parseDate(iValue: any, iLoose?: boolean) {
return null
}

export function parseDateV3(value: any) {
// Built-in date parser might not be the best, but it likely supports more formats than we do currently and
// it's only used in the loose mode.
const date = new Date(value)
return isNaN(date.valueOf()) ? null : date
}

export function parseDate(value: any, loose?: boolean) {
const v2CompatibleParserResult = parseDateV2Compatible(value, loose)
// If the v2 compatible parser found a valid date, always return it for backwards compatibility
if (v2CompatibleParserResult != null) {
return v2CompatibleParserResult
}
// However, if the v2-compatible parser does not find a valid date and loose mode is enabled, we might try
// to parse the date using other parsers that support more formats.
if (loose === true) {
return parseDateV3(value)
}
return null

Check warning on line 288 in v3/src/utilities/date-parser.ts

View check run for this annotation

Codecov / codecov/patch

v3/src/utilities/date-parser.ts#L288

Added line #L288 was not covered by tests
}

/**
* Returns true if the specified value is a string that can be converted to a
* valid date.
Expand All @@ -279,5 +300,5 @@ export function isDateString(iValue: any, iLoose?: boolean) {
return false
}
return spec.regex.test(iValue)
})
}) || (!!iLoose && parseDateV3(iValue) != null)
}

0 comments on commit 430e488

Please sign in to comment.