Regex: Date (YYYY-MM-DD)
The ISO 8601 date, as in 2026-10-03, sorts correctly as text and reads the same in every country. The pattern checks its shape: four digits for the year, a month from 01 to 12 and a day from 01 to 31, all with leading zeros.
Pattern
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ How it works
- ^
- Start of the string.
- \d{4}
- The year: four digits.
- -
- A dash.
- (0[1-9]|1[0-2])
- The month:
01to09, or10to12. - -
- A dash.
- (0[1-9]|[12]\d|3[01])
- The day:
01to09,10to29,30or31. - $
- End of the string.
Matches
- 2026-10-03
- 1999-12-31
- 2024-02-29
Doesn't match
- 2026-13-01
- 2026-1-5
- 26-10-03
- 2026-10-32
In your language
- JavaScript
/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/A literal;
new RegExp(source, flags)builds the same from a string.- Python
re.compile(r"^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$", re.ASCII)A raw string, so backslashes reach
reas written.re.ASCIIkeeps\dto0-9, as in JavaScript (Python matches any Unicode digit otherwise). Usere.fullmatchto test a whole string.- Java
Pattern.compile("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$")A normal string literal, so every backslash is doubled.
matcher(s).matches()tests the whole string.- Go
regexp.MustCompile(`^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$`)A raw string in backticks. RE2 runs in linear time but has no lookaround and no backreferences.
- PHP
preg_match('/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/D', $input)PCRE with
/delimiters inside a single-quoted string. Without theDmodifier,$also matches before a final newline, so it is added to patterns that end in$.- C#
new Regex(@"^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$", RegexOptions.ECMAScript)A verbatim string: backslashes stay, a quote is doubled.
RegexOptions.ECMAScriptkeeps\dto0-9, as in JavaScript.$also matches before a final newline; to reject one, end the pattern with\zinstead.
Common mistakes
February 31 passes
The pattern does not know month lengths, so
2026-02-31and2025-02-29match. Parse the string after matching: in Python,date.fromisoformatraises on an impossible date; in JavaScript, build the date and check that the month did not roll over.ISO 8601 is wider than this
Week dates (
2026-W40-6), ordinal dates (2026-276) and dates with a time (2026-10-03T14:30:00Z) are ISO 8601 too. Accept only what your input should contain.A date is not a moment
Without a time zone,
2026-10-03is a calendar day, not an instant.new Date("2026-10-03")in JavaScript reads it as midnight UTC, which is still the evening before in the Americas.