Regex for Date Format Validation — DD/MM/YYYY, MM-DD-YYYY, and ISO 8601
Date formats differ by region and system — India defaults to DD/MM/YYYY, the US uses MM/DD/YYYY, and APIs/databases standardize on ISO 8601's YYYY-MM-DD. This guide gives you regex patterns for each format and explains exactly where regex validation stops being sufficient.
DD/MM/YYYY (India, UK, most of the world)
This format is the default in India. The pattern below restricts the day to 01-31 and month to 01-12, and accepts either / or - as a separator:
const ddmmyyyy = /^(0[1-9]|[12][0-9]|3[01])[/-](0[1-9]|1[0-2])[/-](19|20)\d{2}$/;
ddmmyyyy.test('25/12/2025'); // true
ddmmyyyy.test('31-04-2025'); // true — regex accepts it, but April has only 30 days!
ddmmyyyy.test('32/01/2025'); // false — day out of range 01-31MM-DD-YYYY (United States)
Same structure, month and day swapped — easy to confuse with DD/MM/YYYY when working with US-based APIs or CSV exports:
const mmddyyyy = /^(0[1-9]|1[0-2])[/-](0[1-9]|[12][0-9]|3[01])[/-](19|20)\d{2}$/;
mmddyyyy.test('12/25/2025'); // true
mmddyyyy.test('25/12/2025'); // false — 25 is not a valid monthISO 8601 (YYYY-MM-DD)
This is the unambiguous format used by SQL DATE columns, JSON APIs, and log timestamps — always prefer it internally, even if your UI displays DD/MM/YYYY:
const iso8601 = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
iso8601.test('2025-06-01'); // true
iso8601.test('2025-13-01'); // false — month 13 doesn't exist
iso8601.test('2025-6-1'); // false — requires zero-padded month/dayWhy Regex Cannot Fully Validate a Real Calendar Date
Regex only checks that digits fall within fixed numeric ranges — it has no concept of "April has 30 days" or "2025 is not a leap year." That means patterns above will happily accept impossible dates like 31/04/2025 or 29/02/2025 (2025 is not a leap year). To fully validate a date, follow the regex check with an actual date library check:
function isRealDate(day, month, year) {
const date = new Date(year, month - 1, day);
return (
date.getFullYear() === year &&
date.getMonth() === month - 1 &&
date.getDate() === day
);
}
isRealDate(31, 4, 2025); // false — April only has 30 days, JS rolls it to May 1
isRealDate(29, 2, 2024); // true — 2024 is a leap year
isRealDate(29, 2, 2025); // false — 2025 is not a leap yearThis trick works because JavaScript's Date constructor "rolls over" invalid day/month combinations into the next valid date instead of throwing — so comparing the parsed components back against your input reliably detects the rollover.
Recommended Validation Pipeline
- Run the regex first to reject obviously malformed strings fast (wrong separators, wrong digit counts)
- Extract day, month, year as numbers from the regex match groups
- Construct a
Dateobject and compare components back, as shown above, to catch impossible dates - For business rules (e.g. "date must be in the future", "age must be 18+"), add explicit comparisons after parsing
- Always store and transmit dates in ISO 8601 internally — only format to DD/MM/YYYY at the UI layer
Frequently Asked Questions
A practical pattern is ^(0[1-9]|[12][0-9]|3[01])[/](0[1-9]|1[0-2])[/](19|20)\d{2}$, which restricts the day to 01-31 and the month to 01-12, but still cannot catch invalid combinations like 31/04.
The pattern ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$ validates the YYYY-MM-DD structure used by ISO 8601, SQL DATE columns, and most APIs.
Regex works on fixed character patterns and has no concept of calendar rules, so it cannot know that April has 30 days, that February has 28 or 29 depending on leap years, or that a specific day-month combination like 30 February is impossible. Use a date library like the built-in Date object, date-fns, or Luxon after the regex format check to confirm the date is real.