Skip to content
d.devtul.fun
中文
Regex · Date

Date Regex

A date validation pattern you can copy, plus the hard truth that regex cannot tell whether a date actually exists.

The pattern

^\d{4}-\d{2}-\d{2}$

Matches the ISO 8601 calendar-date shape YYYY-MM-DD. It checks the digit layout only; it cannot reject February 30 or an impossible month.

Test this pattern

Edit the text below — matching happens locally, nothing is uploaded.

Variants

US format MM/DD/YYYY
^(0?[1-9]|1[0-2])/(0?[1-9]|[12]\d|3[01])/\d{4}$

Matches US slashed dates. Still cannot reject Feb 30; it only bounds each field to a plausible range.

EU format DD-MM-YYYY
^(0?[1-9]|[12]\d|3[01])-(0?[1-9]|1[0-2])-\d{4}$

Matches day-first European dates. Ambiguous with US format for many values, which is itself a reason to avoid slashes.

The date regex you can copy

The safest date shape is the ISO 8601 calendar date, and the regex for its surface form is tiny:

^\d{4}-\d{2}-\d{2}$

It demands four digits, a hyphen, two digits, a hyphen, two digits. That is the entire structural promise. It says nothing about whether the 30th of February exists, because regular expressions do not do arithmetic.

What this pattern actually matches

  • \d{4} — the year, four digits, no sign, no limit. 0000 and 9999 both pass.
  • - — a literal hyphen separator, the ISO choice. (Slashes are not ISO and invite ambiguity.)
  • \d{2} — the month, two digits. 00 and 13 both pass because the pattern only counts digits.
  • \d{2} — the day, two digits, with the same blindness to reality.

The pattern is a gatekeeper for format, not for truth.

US and EU formats, and why they collide

Outside ISO you meet two local conventions, and they disagree on which number is the month:

  • US MM/DD/YYYY: ^(0?[1-9]|1[0-2])/(0?[1-9]|[12]\d|3[01])/\d{4}$
  • EU DD-MM-YYYY: ^(0?[1-9]|[12]\d|3[01])-(0?[1-9]|1[0-2])-\d{4}$

The collision is the famous 03/04/2026 accident. In the US it is March 4. In most of the world it is April 3. If your form silently assumes one and the user assumed the other, records get entered a month off and nobody notices until much later. This ambiguity alone is a strong argument for defaulting to ISO everywhere.

Why regex cannot catch real dates

This is the core argument, and it is non-negotiable. A regex works on characters, not on the Gregorian calendar. Consider:

  • 2026-02-30 — February never has 30 days, yet the pattern accepts it. The 02 and 30 are individually valid digit pairs.
  • 2026-13-01 — month 13 does not exist, but 13 is just two digits.
  • Leap years: 2024-02-29 is real, 2025-02-29 is not. Deciding which requires modulo-4, modulo-100, modulo-400 arithmetic that no regex expresses without becoming a monstrous alternation.

You can tighten the month to 0[1-9]|1[0-2] and the day to 0[1-9]|[12]\d|3[01], but that still happily admits April 31 and every February 29 in a non-leap year. The shape is bounded; the truth is not.

How far you can push the regex

If you absolutely must reject the worst offenders without a parser, you can hardcode day ceilings per month and special-case February:

^(?:\d{4}-(?:0[13-9]|1[0-2])-(?:0[1-9]|[12]\d|30)|...)$

The honest cost is that the pattern balloons to dozens of alternations, it still needs a leap-year exception bolted on as yet more alternation, and it becomes unmaintainable and easy to get wrong. Every hour spent perfecting it is an hour not spent calling strptime.

Common mistakes and the fix

MistakeSymptomFix
\d{2}-\d{2}-\d{4}Cannot tell month from day; US/EU clashPin the order explicitly, prefer ISO YYYY-MM-DD
^....-..-..$Accepts letters and symbolsUse \d, not dots, for numeric fields
[1-31] for the dayCharacter class is wrong; means 1, 2, or 3Use (0?[1-9]|[12]\d|3[01])
No anchorMatches a date buried in other textKeep ^ and $

What to use instead

Hand the date to a real parser, which knows the calendar. In Python:

from datetime import datetime

def parse_date(s):
    d = datetime.strptime(s, "%Y-%m-%d")
    # guard against 2026-02-30 being normalized to March 2
    if d.strftime("%Y-%m-%d") != s:
        raise ValueError("date does not exist")
    return d

In JavaScript, parse then read back and compare, because new Date('2026-02-30') silently rolls over to March:

function parseDate(s) {
  const d = new Date(s + "T00:00:00");
  if (d.getFullYear() + "-" + String(d.getMonth()+1).padStart(2,"0") + "-" + String(d.getDate()).padStart(2,"0") !== s)
    throw new Error("date does not exist");
  return d;
}

The parser catches what the regex cannot: that the 30th of February is fiction.

Generating versus parsing

If you are producing dates rather than checking user input, do not build the string by concatenation and a regex. Format with the language's date formatter so the separator and zero-padding are always correct, then store it in an unambiguous format (UTC, ISO) and only localize at the display layer. The regex's only honest role remains a cheap pre-filter that rejects obviously malformed input before the parser does the real work. When you are tempted to add yet another branch to your date pattern, ask whether the parser already does it in one line — almost always, it does.

Time zones and full ISO 8601

Dates often grow a time. The full ISO 8601 form looks like 2026-02-28T14:30:00Z or 2026-02-28T14:30:00+05:00. A regex for that is even longer and even less trustworthy around leap seconds and mixed offsets. Again, parse it: every modern language has an ISO datetime parser (datetime.fromisoformat, new Date with care, or a library like Luxon). Validate with a parser, format with a parser, and let the regex retire to the one job it is good at — checking the rough shape before you bother the parser at all.

Frequently asked

What is the best date regex?

For shape only, ^\d{4}-\d{2}-\d{2}$ in ISO format. For correctness, do not use regex at all; use a date parser.

Can a regex reject February 30?

No. Regex only bounds digits; it cannot do calendar arithmetic or handle leap years. A parser is required.

Why is MM/DD/YYYY dangerous?

It collides with DD/MM/YYYY: 03/04/2026 is March 4 in the US but April 3 in most of the world. Prefer ISO YYYY-MM-DD.

How do I validate a date in JavaScript?

Parse with new Date and then read the components back and compare to the input string; otherwise Feb 30 silently becomes March 2.

Related regex guides

Open the full Regex Tester