Quick answer
The regex for a yyyy-mm-dd date is ^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$. It requires a four-digit year, a two-digit month from 01 to 12 and a two-digit day from 01 to 31, so 2026-10-12 matches and 2026-13-01 or 2026-1-5 do not. It cannot know that February has 28 or 29 days, so confirm the date with a real date parser afterwards.
- Tested examples
- Token-by-token breakdown
- Code in 5 languages
- Live tester
How to use the date (yyyy-mm-dd) regex
- 1Copy the pattern with Copy pattern in the tester, or take a ready-made snippet for your language from the code section below.
- 2Check the sample lines in Test string: every line the date (yyyy-mm-dd) regex accepts is highlighted, rejected lines stay plain.
- 3Paste your own values one per line to see which pass, and read Explanation for what each part of the pattern does.
- 4Open the Code tab for JavaScript, Python, Go, C#, PHP or Dart with the pattern translated and escaped.
- 5Validate again on the server: client-side checks give users quick feedback, not security.
How does the date (yyyy-mm-dd) regex work?
Read it left to right. Each piece below matches one part of the input:
^\d{4}Start of the string and a four-digit year.-A hyphen. ISO 8601 uses hyphens; slashes are rejected.(?:0[1-9]|1[0-2])The month: 01 to 09, or 10 to 12. Single digits are not allowed.-Another hyphen.(?:0[1-9]|[12]\d|3[01])The day: 01 to 09, 10 to 29, or 30 and 31.$End of the string: no time part.
Which strings does the date (yyyy-mm-dd) regex match?
The whole input must match, from the first character to the last. Every row below is checked against the pattern by our automated test suite before each release.
| Input | Matches? | Why |
|---|---|---|
2026-10-12 | Yes | A normal date. |
1999-01-01 | Yes | January 1st. |
2024-02-29 | Yes | A real leap day. |
2026-02-31 | Yes | Right shape, impossible date: only a date parser catches this. |
2026-13-01 | No | There is no month 13. |
2026-00-10 | No | Or month 00. |
2026-10-32 | No | Days stop at 31. |
2026-1-5 | No | Month and day need two digits. |
26-10-12 | No | The year needs four digits. |
2026/10/12 | No | Slashes instead of hyphens. |
2026-10-12T09:30:00Z | No | A full timestamp: remove the $ to allow a time part. |
How do I reject impossible dates like February 31?
Let the regex check the shape, then let a date library check the calendar. In JavaScript, build the date in UTC and make sure nothing rolled over:
function isRealDate(s) {
if (!/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/.test(s)) return false;
const [y, m, d] = s.split("-").map(Number);
const date = new Date(Date.UTC(y, m - 1, d));
return date.getUTCMonth() === m - 1 && date.getUTCDate() === d;
}Date.UTC(2026, 1, 31) silently becomes March 3rd, so the month check catches it. In Python, datetime.date.fromisoformat(s) raises ValueError for impossible dates; in Go, time.Parse("2006-01-02", s) does the same. A leap-year-aware regex exists, but it is several lines long and harder to review than these three.
Why use the yyyy-mm-dd format at all?
Because it is unambiguous and sorts correctly. 03/04/2026 is March 4th in the US and April 3rd almost everywhere else; 2026-03-04 means the same date to everyone. It is the calendar-date format of ISO 8601 and of RFC 3339, which is what JSON APIs and <input type="date"> use. Strings in this format also sort alphabetically in date order, which makes file names and database keys easy to work with.
To restrict the year range, replace \d{4} with something like (?:19|20)\d{2} for 1900–2099.
How do I use the date (yyyy-mm-dd) regex in JavaScript, Python, Go, C# and PHP?
The pattern is translated for each engine rather than pasted as is: named groups, anchors, escapes, shorthand classes and flags are adjusted so each snippet behaves as closely to the JavaScript version as its engine allows. Differences that remain (Go's RE2 has no lookarounds or backreferences, for example) are called out under each one. The Code tab in the tester above also exports Dart, and updates as you edit the pattern.
JavaScript and TypeScript
const regex = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;
function isValid(value) {
return regex.test(value);
}
console.log(isValid("2026-10-12"));Python
import re
pattern = re.compile(r"^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])\Z")
def is_valid(value: str) -> bool:
return pattern.search(value) is not None
print(is_valid("2026-10-12"))- \d and \w are written as [0-9] and [A-Za-z0-9_] because in Python they also match digits and letters from other scripts.
- JavaScript's $ only matches at the very end of the string. Python's $ also matches before a trailing newline, so the pattern uses \Z instead.
Go
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$`)
fmt.Println(re.MatchString("2026-10-12"))
}C#
using System;
using System.Text.RegularExpressions;
// The timeout stops a pathological input from hanging the thread (ReDoS).
var regex = new Regex(@"^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])\z", RegexOptions.None, TimeSpan.FromSeconds(1));
Console.WriteLine(regex.IsMatch("2026-10-12"));- \d and \w are written as [0-9] and [A-Za-z0-9_] because in .NET they also match digits and letters from other scripts.
- JavaScript's $ only matches at the very end of the string. .NET's $ also matches before a trailing newline, so the pattern uses \z instead.
PHP
<?php
$pattern = '/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/D';
function isValid(string $value, string $pattern): bool
{
return preg_match($pattern, $value) === 1;
}
var_dump(isValid('2026-10-12', $pattern));- The D modifier makes $ match only at the very end, like JavaScript (PCRE's $ also matches before a trailing newline).
Which related regex patterns are useful?
- Regex for 24-Hour Time (HH:MM): Hours 00–23, minutes 00–59 and optional seconds, like 09:30 or 23:59:59.
- Regex for Numbers Only: Digits only, plus variants for signs, decimals and thousands separators.
- Regex for Semantic Versioning (SemVer): major.minor.patch with optional prerelease and build metadata, per semver.org.
- Regex for UUIDs (Versions 1–8): 8-4-4-4-12 hex layout with a real version digit (1–8) and the standard variant.
Browse all 25 patterns in the regex pattern library, or build your own in the regex tester.
Frequently asked questions
Last updated by the Playxoft engineering team.