Date Regex for YYYY-MM-DD
Match a date written year-month-day with a four-digit year, a month from 01 to 12 and a day from 01 to 31, each captured in a named group.
One case per line; the m flag makes ^ and the end anchor work line by line.
Link optionspattern only
The address bar holds the pattern, flags and replacement, so Copy link shares them. Your test text stays out of it unless you include it (up to 2,000 characters), because it may be private. Nothing is sent to a server.
Matches
6matches
The first is “2026-10-08”, at position 0.
- Groups
- year, month, day
- Characters matched
- 60 of 138
What the pattern meansHover or tap a part to see it in the pattern and what it matched
How this works: Method, 6 sources, Checked against 1 worked example,
How this works
Method
Your pattern runs in your own browser’s JavaScript engine, in a background worker that is stopped after 1.5 seconds, so a pattern that backtracks catastrophically can’t freeze the page. The explanation comes from our own parser of the ECMAScript pattern grammar, checked against the engine; what each part matched is found by wrapping that part in one more group and running the pattern again. Conversions to other flavours only rewrite the syntax and list what their documentation says works differently.
Sources
How it’s tested
One worked example for this page is checked by automated tests before every release: given the inputs, the tool must show the expected answer.
Changes
- First published, with a library of 19 common patterns and their test cases.
Worked example
Take 2026-10-08, the first case in the tester. Reading the pattern left to right, each part takes its share of the text:
^The start of the text (matches a position, no characters)(?<year>\d{4})Group 1 “year”: matches 2026-“-” matches -(?<month>0[1-9]|1[0-2])Group 2 “month”, either of two alternatives: matches 10-“-” matches -(?<day>0[1-9]|[12]\d|3[01])Group 3 “day”, either of three alternatives: matches 08$The end of the text (matches a position, no characters)
How the YYYY-MM-DD regex works
The pattern reads left to right in the same order as the date. \(?<year>\d{4})\ takes exactly four digits and names the group "year", so in code you can read \match.groups.year\ instead of counting brackets. A literal hyphen follows. The tester above colours each named group differently, so you can see which part of the date each group caught.
The month is an alternation: \0[1-9]\ covers 01 to 09 and \1[0-2]\ covers 10 to 12, so 00 and 13 can never get through. The day uses three branches: \0[1-9]\ for the first nine days, \[12]\d\ for 10 to 29, and \3[01]\ for 30 and 31. The anchors at both ends make the whole string the date, so "2026-10-08T09:30" or "x2026-10-08" is rejected.
What the regex cannot do is count. It doesn't know that April has 30 days or that 2025 is not a leap year, so 2025-02-30 and 2026-04-31 both pass. Writing those rules into a regex is possible but turns a readable line into a wall of alternations. The usual answer is two steps: the regex checks the shape, then code checks the calendar. Build the date with \new Date(Date.UTC(y, m - 1, d))\ and confirm that its year, month and day come back unchanged; JavaScript quietly rolls February 30 over into March, so a mismatch means the date doesn't exist.
- RFC 3339, the internet profile of ISO 8601, writes a full date as four-digit year, two-digit month (01–12) and two-digit day, whose limit (28 to 31) depends on the month and year. Source: RFC 3339, section 5.6.
- Date.parse("2014-02-30") returns 2 March 2014 in Chrome but NaN in Firefox: handling of invalid dates is implementation-specific. Source: MDN: Date.parse().
- The Date constructor carries overflowing values into the next unit, so new Date(1990, 12, 1) is 1 January 1991. Source: MDN: Date() constructor.
Test cases
Every case runs as an automated test of this page’s pattern, so the table can’t drift from what the pattern really does.
| Text | Result | Why |
|---|---|---|
| 2026-10-08 | Passes | an ordinary date |
| 1969-07-20 | Passes | the Moon landing |
| 2024-02-29 | Passes | a real leap day |
| 2000-01-01 | Passes | the lowest month and day |
| 1999-12-31 | Passes | the highest month and day |
| 2025-02-30 | Passes | passes the regex, yet February never has a 30th: check with Date |
| 2026-13-01 | Fails | there is no 13th month |
| 2026-00-10 | Fails | months start at 01 |
| 2026-1-5 | Fails | month and day need two digits |
| 26-10-08 | Fails | a two-digit year |
| 2026/10/08 | Fails | slashes instead of hyphens |
| 2026-10-32 | Fails | no month has 32 days |
| 08-10-2026 | Fails | day first, the other way round |
What it doesn’t check
- It accepts impossible dates such as 2025-02-30 and 2026-06-31; check the calendar in code after the match.
- Years 0000 to 9999 all pass, so add a range check if only, say, birth dates after 1900 make sense.
- It does not accept a time or time zone after the date; ISO 8601 date-times need a longer pattern or a date library.
- Browsers disagree about impossible dates: Chrome turns 2014-02-30 into 2 March while Firefox returns an invalid date, so don’t rely on the Date parser alone either.
The same pattern in other languages
Converted automatically from the JavaScript version; the tester above always runs JavaScript.
| Flavour | Pattern | Notes |
|---|---|---|
| Python | (?a)^(?P<year>\d{4})-(?P<month>0[1-9]|1[0-2])-(?P<day>0[1-9]|[12]\d|3[01])\Z | Named groups are written (?P<year>…) in Python’s re module, and the match’s groupdict() returns them as a dictionary. |
| PCRE | ^(?<year>\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12]\d|3[01])\z | |
| Go | ^(?P<year>\d{4})-(?P<month>0[1-9]|1[0-2])-(?P<day>0[1-9]|[12]\d|3[01])$ | Go’s regexp accepts (?P<name>…) and, from Go 1.22, (?<name>…); use SubexpNames to find each group’s index. |
| Java | ^(?<year>\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12]\d|3[01])\z | |
| .NET | ^(?<year>\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12]\d|3[01])\z | In .NET, \d and \w match any Unicode digit or letter. Pass RegexOptions.ECMAScript, or write [0-9] and [A-Za-z0-9_], for JavaScript’s ASCII-only meaning. |
Sources
Frequently Asked Questions
Can a regex check that a date is valid?
It can check the format and keep months between 01 and 12 and days between 01 and 31. It can’t sensibly know month lengths or leap years, so confirm the date in code after it matches.
How do I get the year, month and day out of the match?
The pattern uses named groups, so the match object has a groups property with year, month and day as strings. Convert them with Number before doing arithmetic.
Why use YYYY-MM-DD rather than DD/MM/YYYY?
Year-month-day is the ISO 8601 order: it sorts correctly as plain text and can’t be misread the way 03/04 can. It is also the format of the value an HTML date input gives you.
Does this regex accept dates with times?
No. Both ends are anchored, so a T and a time after the date make it fail. Split the date part off first or use a pattern built for full date-times.