Skip to main content
Regex Guide

Regex for Dates
ISO, US, EU and Custom Formats

Patterns that actually work, the February-30 problem regex cannot solve, and how to extract dates from unstructured text.

7 min read·Updated May 2026

Regex for dates is one of those tasks that looks simple until you encounter February 30th, two-digit years, or a log file that mixes three formats on the same line. This guide gives you working patterns for the most common date formats, explains what regex cannot validate, and shows how to extract dates from free text in JavaScript and Python.

ISO 8601: YYYY-MM-DD

ISO 8601 is the only date format that sorts correctly as a string and is unambiguous internationally. Match it with:

# Basic — accepts any digit in range
\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])

# Anchored for full-string validation
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$

# With optional time and timezone (full ISO 8601)
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$
2026-05-02
Valid date
2026-12-31
Last day of year
2026-01-01
First day of year
2026-13-01
Month 13 — rejected by (0[1-9]|1[0-2])
2026-00-15
Month 00 — rejected
2026-05-32
Day 32 — rejected by 3[01]
2026-02-30
Feb 30 — passes regex, fails calendar
26-05-02
Two-digit year — rejected by \d{4}

US Format: MM/DD/YYYY

The US format uses slashes and puts the month first. The pattern below accepts slashes, hyphens, or dots as separators and handles one- or two-digit months and days:

# MM/DD/YYYY — slash, hyphen, or dot separator
(0?[1-9]|1[0-2])[/.-](0?[1-9]|[12]\d|3[01])[/.-]\d{4}

# Anchored, slash only, strict two-digit padding
^(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{4}$
The separator consistency problem
If you allow any separator with [/.-], your pattern will match 01/15-2026 — a mix of separators. Use a backreference to enforce consistency: (0?[1-9]|1[0-2])([/.-])(0?[1-9]|[12]\d|3[01])\2\d{4} — the \2 back-references whatever separator the first group captured.

EU Format: DD/MM/YYYY

The EU format puts the day first. The regex structure is the same as US with month and day swapped:

# DD/MM/YYYY
(0?[1-9]|[12]\d|3[01])[/.-](0?[1-9]|1[0-2])[/.-]\d{4}
You cannot tell US from EU by regex alone
01/02/2026 is January 2nd in the US and February 1st in Europe. Both patterns match it. The only solution is to know your data source and apply the correct pattern — or use ISO 8601 everywhere.

What Regex Cannot Validate: The February 30 Problem

Regex can enforce that months are 01–12 and days are 01–31, but it cannot check whether a specific day is valid for a specific month. February 30th, April 31st, and June 31st all pass a regex check.

// JavaScript: regex match, then parse to catch invalid calendar dates
function isValidDate(str) {
  const pattern = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
  if (!pattern.test(str)) return false;
  // regex passed — now check the calendar
  const d = new Date(str);
  return d instanceof Date && !isNaN(d) && d.toISOString().startsWith(str);
}

isValidDate('2026-02-30'); // false — regex passes, Date rejects
isValidDate('2026-05-02'); // true
import re
from datetime import date

def is_valid_iso_date(s: str) -> bool:
    if not re.fullmatch(r'\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])', s):
        return False
    try:
        date.fromisoformat(s)
        return True
    except ValueError:
        return False

is_valid_iso_date('2026-02-30')  # False
is_valid_iso_date('2026-05-02')  # True

Extracting Dates from Free Text

When you need to pull dates out of a paragraph, log line, or document — rather than validate a single field — remove the anchors and use findall / matchAll:

import re

text = """
Order placed on 2026-01-15. Shipped 2026-01-18.
Expected delivery: 2026-01-22. Invoice date: 2026-01-15.
"""

dates = re.findall(r'\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])', text)
# ['2026-01-15', '2026-01-18', '2026-01-22', '2026-01-15']

# Deduplicate while preserving order
unique_dates = list(dict.fromkeys(dates))
# ['2026-01-15', '2026-01-18', '2026-01-22']
// JavaScript: extract ISO dates from a string
const text = `Report generated 2026-03-01. Data from 2026-02-01 to 2026-02-28.`;
const pattern = /\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])/g;
const dates = [...text.matchAll(pattern)].map(m => m[0]);
// ['2026-03-01', '2026-02-01', '2026-02-28']

Timestamps and Datetime Strings

Log files typically include a time component. Here are patterns for common timestamp formats:

Format Example Pattern
ISO 8601 datetime 2026-05-02T14:30:00Z \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z
ISO with ms 2026-05-02T14:30:00.123Z \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z
Apache CLF 02/May/2026:14:30:00 +0000 \d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} [+-]\d{4}
Syslog May 2 14:30:00 \w{3}\s+\d{1,2} \d{2}:\d{2}:\d{2}
US datetime 05/02/2026 14:30:00 \d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}
Unix epoch (seconds) 1746191400 \b1[0-9]{9}\b

Common Mistakes

Forgetting anchors for field validation
Problem: Without ^ and $, the pattern \d{4}-\d{2}-\d{2} matches "2026-05-02" inside "version-2026-05-02-final.zip" — not what you want when validating a date field.
Fix: Use ^...$ for validation. Omit anchors only when extracting from free text.
Using \d{2} for month and day without range checks
Problem: \d{2} matches 00 and 99. The pattern \d{4}-\d{2}-\d{2} accepts 2026-99-99.
Fix: Use (0[1-9]|1[0-2]) for months and (0[1-9]|[12]\d|3[01]) for days.
Assuming a two-digit year is safe
Problem: The pattern \d{2}/\d{2}/\d{2} matches 01/02/03 — but is that 2001, 2003, or 1901? Two-digit years are ambiguous by definition.
Fix: Require four-digit years (\d{4}) at boundaries where the data format is under your control.
Validating dates with regex alone
Problem: Even a thorough pattern accepts 2026-02-30 and 2026-04-31.
Fix: Parse the matched string with a date library after the regex match. Regex is for format; the library is for calendar validity.

Test Your Date Pattern

Paste a date regex and test it against real inputs — including edge cases like Feb 30th and mixed separators.

Open Regex Tester →

Related