Do not depend on new Date()
.
All of these but the last fails. They create date objects, just not on the date you expect.
console.log(new Date(2023,02,35))console.log(new Date(2023,02,29))console.log(new Date(2023,01,29))console.log(new Date(2024,02,39))console.log(new Date(Date.parse('2023-02-30')))console.log(new Date(Date.parse('2023-02-29')))console.log(new Date(Date.parse('2023-02-31')))console.log(new Date(Date.parse('2023-02-32'))) // fails (null)
So I wrote a function that actually validates the date, with a few more features
type ns = number | `${number}`;class SiteDateLib { static validate(_options: { /** two digit years will create a date in the year 19xx by native-default. This can override that. You can also change the default value of options.defaultCentury to 20 or whatever you like */ defaultCentury?: ns, yearMustStartWith?: ns } | ns, ...parts: [year?: ns, _month?: ns, day?: ns, hour?: ns, minute?: ns, second?: ns]) { if (!/^\d+/.test(parts.join(''))) return null; const hasOptions = typeof _options === 'object'; const options = hasOptions ? _options : {}; options.defaultCentury = options.yearMustStartWith ?? options.defaultCentury ?? 19; let _year = `${hasOptions ? parts.shift() : _options}`; if (_year.length < 3) { _year = _year.padStart(4, `${options.defaultCentury}0`); } if (options.yearMustStartWith && !_year.startsWith(options.yearMustStartWith)) { return null; } //day cannot be 0 if ((''+parts[1]) === '0') return null; const [_month, _day, hour, minute, second] = [...parts, 0, 0, 0, 0, 0].map((v) => +(v || 0)); const day = _day || 1 const month = _month - 1; const year = +_year const date = new Date(year, month, day, hour, minute, second); switch (true) { case date.getFullYear() !== year: case parts[0] && date.getMonth() !== month: case parts[1] && date.getDate() !== day: case parts[2] !== undefined && date.getHours() !== hour: case parts[3] !== undefined && date.getMinutes() !== minute: case parts[4] !== undefined && date.getSeconds() !== second: return null } return isNaN(+date) ? null : date; }};//test SiteDateLib.validate with bad tests like negative numbers and numbers too high...// Good Testsconsole.log('good', SiteDateLib.validate(2021, 5, 31, 23, 59, 59)?.toString()) // Should return a Date objectconsole.log('good', SiteDateLib.validate(4, 1, 1, 0, 0, 0)?.toString()) // Should return a Date objectconsole.log('good', SiteDateLib.validate(2021, 6, 15)?.toString()) // Should return a Date objectconsole.log('good', SiteDateLib.validate(2021, 6, 15, 14, 30)?.toString()) // Should return a Date objectconsole.log('good', SiteDateLib.validate(2020, 2, 29)?.toString()) // Leap year valid date, should return a Date objectconsole.log('good', SiteDateLib.validate(2000, 2, 29, 12, 0, 0)?.toString()) // Century leap year, should return a Date objectconsole.log('good', SiteDateLib.validate(2021, 11, 30)?.toString()) // End of month, should return a Date objectconsole.log('good', SiteDateLib.validate(2021, 7, 4, 9, 15)?.toString()) // Valid date and time, should return a Date objectconsole.log('good', SiteDateLib.validate(1999, 12, 31, 23, 59, 59)?.toString()) // Y2K eve, should return a Date objectconsole.log('good', SiteDateLib.validate({ yearMustStartWith: 19 }, 22, 3, 15, 8)?.toString()) // Valid date with hour, should return a Date object// Bad Testsconsole.log('bad', SiteDateLib.validate(2021, 2, 29, 1, 1, 1)?.toString()) // Invalid date, should return nullconsole.log('bad', SiteDateLib.validate(20213, 1, 32, 1, 1, 1)?.toString()) // Invalid date, should return nullconsole.log('bad', SiteDateLib.validate(2021, 13, 1, 1, 1, 1)?.toString()) // Invalid month, should return nullconsole.log('bad', SiteDateLib.validate(21, 0, 1, 1, 1, 1)?.toString()) // Invalid month, should return nullconsole.log('bad', SiteDateLib.validate(6, 1, 35, 1, 1, 1)?.toString()) // Invalid year, should return nullconsole.log('bad', SiteDateLib.validate(16, 94, 1, 1, 1, 1)?.toString()) // Invalid month, should return nullconsole.log('bad', SiteDateLib.validate(24, -3, 1, 1, 1, 1)?.toString()) // Invalid day, should return nullconsole.log('bad', SiteDateLib.validate(23, 29, 1, 1, "hour", 1, 1)?.toString()) // Invalid hour, should return nullconsole.log('bad', SiteDateLib.validate(2021, 1, 1, 1, "minute", 1)?.toString()) // Invalid minute, should return nullconsole.log('bad', SiteDateLib.validate(2021, 1, 1, 1, 1, "second")?.toString()) // Invalid seconds, should return null