1
\$\begingroup\$

I was wondering if it's possible to simplify this regex string (JS):

function validateDateString(date){
    const regex = /[0-1][0-9]-[0-3][0-9]-[0-9]{4} [0-2][0-9]:[0-5][0-9] [A|P][M]/; 
    return regex.test(date)
}

It is supposed to validate a string in the following format: MM-DD-YYYY hh:mm A

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I'm not sure I understand the point of this regex. This accepts invalid dates and substrings like "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\$
    – ggorlen
    May 27, 2021 at 4:29
  • \$\begingroup\$ right, thanks, this is wrong, I have to rewrite \$\endgroup\$
    – Tom
    May 27, 2021 at 22:21

1 Answer 1

2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.