I found this working date validation in legacy code:
return !isNaN(new Date(dateString));
My OCD kicked in. I’d been modernizing the codebase with ES6+, so I “fixed” it:
return !Number.isNaN(new Date(dateString)); // Broke everything!
Invalid dates now passed as valid. Wait… WHAT?!
The truth hit me hard
isNaN() coerces to Number first: isNaN(new Date('')) → true ✅
Number.isNaN() checks literal NaN only: Number.isNaN(new Date('')) → false (it’s an object!) ❌
const invalidDate = new Date('');
console.log(typeof invalidDate); // "object"
console.log(Number(invalidDate)); // NaN
console.log(Number.isNaN(invalidDate)); // false - not literal NaN
console.log(isNaN(invalidDate)); // true - coerces first!
The proper way to check for invalid dates? The legacy code was actually fine, but if I want to do it “explicitly”, I should check the timestamp:
function isValidDate(dateString) {
const date = new Date(dateString);
return !isNaN(date.getTime());
// Or: return date.toString() !== 'Invalid Date';
}
Applied new validation logic and the tests went green again. Thirty minutes of my life I’ll never get back.
How I’m Protecting Myself Going Forward
- Always have tests before AND run them after refactoring - saved me this time
- Use
"use strict";- strict mode catches silent errors and prevents some footguns - Set up ESLint - let the linter catch these issues before they reach production
- Use TypeScript - static typing would have caught this at compile time
- Always use strict equality (
===) - avoid==and other coercion surprises (eg. concatenating with+) - Be explicit with type conversions - use
Number(),String(),Boolean()instead of relying on implicit coercion - Don’t modernize blindly - if it works, understand WHY before changing it (read MDN carefully)
- OCD is not a good reason to change code - consistency is important, but correctness comes first
Maybe tomorrow will be better. Maybe.