Some minor tips:
- Add "^" and "$" to your regex to ensure your input string doesn't contain anything but a date
- No brackets are needed around the
[M]
- A simple M
means the same thing
- Instead of checking for AM/PM with
[A|P][M]
, keep the AM/PM characters together, like this: (AM|PM)
- it makes it much more readable.
- Since it's impossible to accurately test numeric ranges of a date-string in regex, I wouldn't even attempt it. If you really want to ensure the numbers fall into a valid range, do so after the fact, outside of the regex. Inside the regex, you can simply use
\d
to match any digit.
After applying these tips, you'll be left with a pretty simple regex that looks like this:
/^\d\d-\d\d-\d\d\d\d \d\d:\d\d (AM|PM)$/
As an aside, you might notice it's possible to remove a little repetition in the regex with \d{2}
or \d{4}
, but don't bother. There's not really a lot of repetition, and the {2}
/{4}
will just obfuscate what you're trying to accomplish here.
Depending on your use case, simply using the above regex might be good enough, but if you also want to ensure the numbers fall into a valid range, I would actually parse the numbers out of the date-string, feed them into Javascript's Date class, and verify the numbers using the date instance. Relying on the built-in Date class will help with edge cases, such as leap years. In the below code snippet I am assuming that these date-strings are in UTC
function validateDateString(dateStr) {
const regex = /^(\d\d)-(\d\d)-(\d\d\d\d) (\d\d):(\d\d) (?:AM|PM)$/;
const match = regex.exec(dateStr);
if (!match) return false;
const [month, day, year, hr, min] = match.slice(1).map(Number);
const date = new Date(Date.UTC(year, month-1, day, hr, min));
return (
year === date.getUTCFullYear() &&
month === date.getUTCMonth() + 1 &&
day === date.getUTCDate() &&
hr === date.getUTCHours() &&
min === date.getUTCMinutes()
);
}
console.assert(validateDateString('11-30-1999 12:34 AM'));
console.assert(!validateDateString('13-30-1999 12:34 AM'));
console.assert(!validateDateString('1x-30-1999 12:34 AM'));
console.log('Done')
The important thing to understanding how the above code snippet works, is to understand what happens when you give new Date()
(or in this case, date.UTC()
) a too-big number. If, for example, you passed in 5
for the hours and 70
for the minutes, then it'll normalize the values by moving an hour worth of time from the minutes to the hours, leaving it with 6
as the hours and 10
as the minutes.
Here's what this looks like in action:
> const d = new Date(0, 0, 0, 5 /* hours */, 70 /* minutes */)
> d.getHours()
6
> d.getMinutes()
10
In the above example, it's easy enough to test that a numeric overflow happened, we just need to compare our input numbers with our outputs. We can quickly see that 5 !== 6
and 70 !== 10
, so some sort of overflow must have happened, which means our date string was not valid. This is the same principle being used in the above code snippet - we just test each input with each output to ensure nothing overflowed.
"blah blah 19-39-0000 29:00 AM foobar"
. What is being validated exactly? What application context this is being used in? Thanks for clarifying. \$\endgroup\$