★ Dev formats · regex
Regex for ISO 8601 Date
Checks the calendar-date form of ISO 8601: a four-digit year, month 01–12 and day 01–31. It won't catch impossible dates like 2023-02-30; parse the date to check that.
- Runs in your browser
- No signup
Try the iso 8601 date pattern
Read it in plain English (best-effort)
- ^start of a line
- \da digit (0–9)
- {4}exactly 4 of the previous
- -the character '-'
- (?:start a group (not captured)
- 0the character '0'
- [1-9]one character from: 1–9
- |or
- 1the character '1'
- [0-2]one character from: 0–2
- )end of group
- -the character '-'
- (?:start a group (not captured)
- 0the character '0'
- [1-9]one character from: 1–9
- |or
- [12]one character from: '1', '2'
- \da digit (0–9)
- |or
- 3the character '3'
- [01]one character from: '0', '1'
- )end of group
- $end of a line
One sample per line works well with the m flag on anchored patterns.
The pattern, piece by piece
/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/| Part | What it does |
|---|---|
^\d{4} | four-digit year |
-(?:0[1-9]|1[0-2]) | month 01–12 |
-(?:0[1-9]|[12]\d|3[01]) | day 01–31 |
$ | nothing after the date |
What it accepts and rejects
Every sample below is checked automatically in JavaScript and Python. The tester above is preloaded with all of them, one per line.
| Should match | |
|---|---|
2026-11-08 | ✓ |
1999-12-31 | ✓ |
2024-02-29 | ✓ |
2000-01-01 | ✓ |
2026-07-15 | ✓ |
2010-10-10 | ✓ |
| Should not match | |
|---|---|
2026-13-01 | ✗ |
2026-00-10 | ✗ |
2026-02-32 | ✗ |
26-11-08 | ✗ |
2026/11/08 | ✗ |
2026-1-8 | ✗ |
2026-11-08T10:00:00Z | ✗ |
Copy it into your code
RE2-safeNo lookarounds or backreferences, so the same pattern works in Go's RE2 engine and in Rust's regex crate.
const isoDateRe = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;
export function isValidIsoDate(value) {
return isoDateRe.test(value.trim());
}When not to use regex for this
Leap years and month lengths are calendar logic. After the format check, parse with your date library and confirm it round-trips to the same string; that rejects 2023-02-30 and 2026-04-31.
Questions engineers actually ask
Why does 2023-02-30 pass?
Does it match timestamps like 2026-11-08T10:00:00Z?
What is catastrophic backtracking?
More dev formats patterns
- UUIDRFC 9562 UUIDs, versions 1–8, with the standard variant bits.^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$
- 24-hour TimeHH:MM or HH:MM:SS in 24-hour format, 00:00 to 23:59:59.^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$
- Semantic VersionThe official semver.org pattern, with pre-release and build metadata.^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
- JWTThree base64url segments starting with eyJ. Shape only, never trust it.^eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$
- Base64Standard padded base64: groups of four with = or == padding.^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
- MAC AddressColon, hyphen or Cisco dot notation, without mixing separators.^(?:(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}|(?:[0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})$
Or browse the full regex library.
More from the workbench
Fine print
Tools: on the house.
Engineer: by the hour.
The regex was free. Code that doesn't need one is the upgrade: typed schemas, validated once at the edge, trusted everywhere after.
I'm Himanshu, an AI and full-stack engineer. I build MVPs, AI agents and the growth plumbing around them for early-stage startups.
