Regex to match a date — every common format
Patterns for ISO, US, and European formats — and the reason none of them validate semantic correctness.
The four common formats
1. ISO 8601 (YYYY-MM-DD)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
Matches: 2024-01-15, 1999-12-31
2. US format (MM/DD/YYYY)
^(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{4}$
Matches: 01/15/2024, 12/31/1999
3. European format (DD/MM/YYYY)
^(0[1-9]|[12]\d|3[01])/(0[1-9]|1[0-2])/\d{4}$
Matches: 15/01/2024, 31/12/1999
4. ISO datetime
^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?$
Matches: 2024-01-15T10:30:00Z, 2024-01-15T10:30:00.123-05:00
The semantic-correctness problem
None of these patterns validate that the date actually exists:
- February 31st — passes regex, isn't a real date
- April 31st — passes regex, doesn't exist
- February 29 in non-leap years — passes regex, isn't valid
Regex is checking structural form, not calendar reality. For real validation, parse with a date library after the regex passes the format check.
Better: regex + date library
Use the regex as a fast format filter, then a library for semantic validation:
JavaScript
function isValidDate(s) {
if (!/^\d{4}-\d{2}-\d{2}$/.test(s)) return false;
const d = new Date(s);
return d.toISOString().slice(0, 10) === s;
}
The round-trip check (parse, then re-format) catches February 31 — it would re-format to March 3, which doesn't match the input.
Python
from datetime import date
import re
def is_valid_date(s):
if not re.match(r"^\d{4}-\d{2}-\d{2}$", s):
return False
try:
date.fromisoformat(s)
return True
except ValueError:
return False
Restricting the year range
If you want to limit to plausible years (say, 1900-2099):
^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
Time formats
24-hour HH:MM:
^([01]\d|2[0-3]):[0-5]\d$
24-hour with seconds:
^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$
12-hour with AM/PM:
^(0?[1-9]|1[0-2]):[0-5]\d\s?(AM|PM|am|pm)$
Extracting dates from prose
To find date-shaped strings in unstructured text:
\b\d{4}-\d{2}-\d{2}\b ISO dates
\b\d{1,2}/\d{1,2}/\d{2,4}\b US or EU dates with slashes
Drop the anchors and use the global flag. The \b word boundaries prevent matching dates inside larger numbers like 1-2024-2025.
Date in English text (much harder)
"January 15, 2024" or "the 15th of January" can't be matched cleanly with regex. For natural-language date parsing, use:
- JavaScript:
chrono-node - Python:
dateparser - Java:
Natty
These handle "next Tuesday", "two weeks ago", "Q3 2024", and dozens of other formats regex can't.
The takeaway
Use a format-specific regex as the first filter, then parse with a date library for semantic validation. Don't try to make a single regex check both — you'll either miss invalid dates or write something unmaintainable.
For natural-language dates, skip regex entirely and use a date-parsing library.
Related reading
Try this pattern in the explainer
Paste any regex into the live explainer and see what each token means, with example matches in real time.
Open the regex explainer →