PatternRegex

Match a date in YYYY-MM-DD

An ISO-style date pattern that actually checks the calendar shape — month 13 and day 32 are rejected, unlike the naive \d{4}-\d{2}-\d{2}.

Last updated

Patterng
\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])
Open in Regex Tester & Builder

Matches

  • 2026-09-04
  • 1999-12-31
  • 2030-01-09

Doesn’t match

  • 2026-13-01
  • 2026-00-12
  • 26-09-04

Why it’s written this way

The month group allows 01–09 or 10–12, and the day group allows 01–09, 10–29, or 30–31 — so the pattern encodes the shape of a calendar, not just "digits with dashes".

That is the practical middle ground: \d{4}-\d{2}-\d{2} happily accepts 2026-13-99, while a pattern that also knew February's length would be unreadable and still wrong every leap year.

Edge cases to know

  • →It accepts 2026-02-31 — per-month day counts are a job for a date parser, not a regex.
  • →It matches the date inside a longer timestamp (2026-09-04T10:00) — anchor with ^…$ to validate a whole field.
  • →Years are unrestricted: 0000 and 9999 both pass.

Part by part

\d{4}
The year, as exactly four digits. No range check — a log from year 0000 or 9999 matches, which is the right trade for a pattern that scans text.
(0[1-9]|1[0-2])
The month, spelled out as two branches so only 01 through 12 can match. This is why 2026-13-01 is rejected.
(0[1-9]|[12]\d|3[01])
The day, in three branches: 01-09, then 10-29, then 30 and 31. The last branch is what caps the value at 31.

Common mistakes

  • \d{4}-\d{2}-\d{2}

    The shape is right and the values are not: this accepts 2026-19-42 without complaint. It is fine for finding date-like strings you already trust, but it is not a check.

    \d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])
  • \d{4}-\d{1,2}-\d{1,2}

    Allowing one-digit months and days accepts 2026-1-5, which is not ISO 8601 and sorts incorrectly as a string — the whole reason to use this format is that zero-padded dates sort chronologically.

  • \d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])

    A two-digit year reintroduces the ambiguity the ISO format exists to remove, and matches the tail of a four-digit year, so 2026-01-01 would report a match starting at the 26.

Questions

Does it reject a date that does not exist, like 2026-02-31?

No. Month lengths and leap years cannot be expressed in a pattern of any reasonable size, so 2026-02-31 matches. Parse the match into a date object and check it survived the round trip if that matters.

Why is it not anchored?

Because it is built to pull dates out of log lines and filenames, where they sit mid-string. Wrap it in ^ and $ when you are validating a single field — the two uses want opposite behaviour from the same pattern.

Will it match a date inside a longer number?

Yes. In "120260-01-015" it will find a match, because nothing stops it starting mid-digit. Add word boundaries at each end if your input mixes dates with long numeric identifiers.

Does it handle the time part of a full timestamp?

No, it stops after the day, so scanning "2026-01-01T09:30:00Z" returns just the date portion. That is often what you want; if not, the ISO 8601 timestamp pattern in this collection covers the whole thing.

Why use YYYY-MM-DD rather than a local date format?

Because it is the one format that is unambiguous everywhere and sorts correctly as plain text. 03-04-2026 is the third of April to most of the world and the fourth of March in the United States, and no amount of validation can recover which was meant once it is written down.

Related in Patterns