Date Regex (YYYY-MM-DD)
The ISO date shape gate plus a range-checking strict variant — honest about calendars, CI-verified, preloaded in the live tester.
Pattern variants
Pragmatic
/^\d{4}-\d{2}-\d{2}$/
What it matches
Four digits, a hyphen, two digits, a hyphen, two digits — the ISO 8601 calendar-date shape.
What it deliberately doesn't
Value ranges: month 13 or day 99 pass; anything unpadded, reordered or carrying extra text fails.
Strict
/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/
What it matches
The same shape with the month pinned to 01-12 and the day to 01-31.
What it deliberately doesn't
Calendar truth — 2026-02-31 still passes; only a date parser knows February.
Verified vectors
| Input | Pragmatic | Strict | Note |
|---|---|---|---|
"2026-08-06" | ✓ matches | ✓ matches | |
"2026-13-01" | ✓ matches | ✗ no match | month 13 — pragmatic checks shape only |
"2026-02-31" | ✓ matches | ✓ matches | format-valid but calendar-impossible |
"2026-00-10" | ✓ matches | ✗ no match | month 00 |
"2026-08-32" | ✓ matches | ✗ no match | day 32 |
"2026-1-5" | ✗ no match | ✗ no match | unpadded fields |
"06-08-2026" | ✗ no match | ✗ no match | day-first order |
"2026/08/06" | ✗ no match | ✗ no match | slash separators |
"20260806" | ✗ no match | ✗ no match | no separators |
"2026-08-06T12:00:00Z" | ✗ no match | ✗ no match | timestamp, not a bare date |
" 2026-08-06" | ✗ no match | ✗ no match | leading space — anchors reject it |
Usage by language
JavaScript
Machine-verified/^\d{4}-\d{2}-\d{2}$/
/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/
Python
Machine-verifiedre.compile(r"^\d{4}-\d{2}-\d{2}$")
re.compile(r"^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$")
Java
Syntax-reviewed — not machine-verifiedPattern.compile("^\\d{4}-\\d{2}-\\d{2}$")
Pattern.compile("^\\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01])$")
Go
Syntax-reviewed — not machine-verifiedregexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
regexp.MustCompile(`^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$`)
C#
Syntax-reviewed — not machine-verifiednew Regex(@"^\d{4}-\d{2}-\d{2}$")
new Regex(@"^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$")
PHP
Syntax-reviewed — not machine-verified'/^\\d{4}-\\d{2}-\\d{2}$/'
'/^\\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01])$/'
Caveats
- This validates format, not the calendar: 2026-02-31 passes even the strict variant — parse with your date library for real validation.
- Anchors make it a whole-field validator; timestamps like 2026-08-06T12:00:00Z fail on purpose.
Related patterns
Do it offline:$ grep -E '^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$' dates.txt
How to use it
- Validate form fields and API payloads with the pragmatic line whenever a parser runs afterward anyway — shape first, semantics second.
- Prefer strict when the string feeds filenames or sort keys directly, where a 13th month would file silently out of order.
- Need the parts and not just a verdict? Add named groups in the tester — (?<y>\d{4}) captures cleanly — and keep each name unique.
Four digits, two and two, hyphen-separated: the ISO 8601 calendar date, the one format that sorts correctly as plain text. The pragmatic gate checks shape alone; strict pins months to 01-12 and days to 01-31. Neither consults a calendar — only a date parser knows February — and this page's eleven CI-verified vectors say so honestly.
Examples
^\d{4}-\d{2}-\d{2}$pattern /^\d{4}-\d{2}-\d{2}$/
2026-08-06 ✓ matches
2026-1-5 ✗ no match
2026/08/06 ✗ no match
2026-08-06T12:00:00Z ✗ no matchOne acceptance, three shape refusals: unpadded fields, slash separators and a trailing time component fall outside the 4-2-2 hyphen grid the anchors enforce.
^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$pragmatic /^\d{4}-\d{2}-\d{2}$/
strict /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/
2026-08-32 pragmatic ✓ matches strict ✗ no match
2026-00-10 pragmatic ✓ matches strict ✗ no match
2026-08-06 pragmatic ✓ matches strict ✓ matchesDay 32 and month 00 sit inside the pragmatic shape and outside the strict ranges — exactly what the range check buys. A real date satisfies both.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Format-valid is not calendar-valid
2026-02-31 passes even strict, by design: encoding month lengths and leap years into a regex trades one line for an unmaintainable page. Parse matched strings with your date library when correctness matters — this pattern's job ends at the shape.
Common errors
Invalid regular expression: /^(?<y>\d{4})-(?<m>\d{2})-(?<m>\d{2})$/: Duplicate capture group name- Copy-pasting the month group to make a day group keeps the name m, and one pattern cannot define (?<m>…) twice. Rename the second copy — (?<d>…) — and the compile succeeds.
FAQ
Why does 2026-08-06T12:00:00Z fail the date regex?
The $ anchor ends the match right after the day, so a timestamp leaves characters behind. Validate full timestamps with an RFC 3339 pattern or a parser rather than loosening this one.
Does the strict variant reject February 30th?
No — fields are range-checked independently, so day 30 passes in every month. Only calendar arithmetic can refuse it — the point where regex hands off to a date library.
Will these patterns match 06/08/2026 or other regional forms?
Deliberately not — separators and field order are pinned to ISO 8601. Convert regional input at the edge and keep YYYY-MM-DD internally; string comparison then doubles as chronological order for free.