Regex for Dates (YYYY-MM-DD)

Date inputs are often used in programming languages to get information from users such as birth dates, flight dates, booking dates, etc. Regular expressions can be used to quickly ensure that these dates entered by a user are valid.

A regular expression for dates (YYYY-MM-DD) should check for 4 digits at the front of the expression, followed by a hyphen, then a two-digit month between 01 and 12, followed by another hyphen, and finally a two-digit day between 01 and 31.

Here’s the regex code that does all this:

/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/Edit with Regexity

This code works for most dates but does exclude invalid days of the month like 2021-02-29 (2021 is not a leap year) or 2021-04-31 (April has only 30 days). These checks must be performed using the tools in your specific programming language of choice.

General Date Format

According to the international standard on dates, ISO 8601, the general date format to use is YYYY-MM-DD, which is easily readable by both humans and computers. This format is also easily sortable in chronological order.

The date should be formatted according to the following criteria:

  • A 4-digit year from 0000 to 9999
  • Followed by a hyphen
  • Followed by a 2 digit month from 01 to 12 (padded with leading zeros if necessary)
  • Followed by a hyphen
  • Followed by a 2 digit day from 01 to 31 (padded with leading zeros if necessary)

The ISO standard also accepts the shorter format YYYYMMDD, which we’ll look at later.

So let’s develop these criteria into a working regular expression.

Regular Expression for YYYY-MM-DD Dates

A date should start with a 4 digit year from 0000-9999. We can specify this using the following:

/\d{4}/Edit with Regexity

The digit character \d will accept any digit from 0 to 9 while the quantifier {4} indicates that we want exactly 4 characters (no more and no less). This accepts past dates from 1 BC (0000) and future dates up to the year 9999. We’ll look at dates before 1 BC at the end of this article.

The 4-digit year is followed by a hyphen (-):

/\d{4}-/Edit with Regexity

This is followed by a two-digit month from 01 to 12, padded with leading zeros if required. It might be tempting to simply use \d{2} here which indicates two digits, but this will accept any month representation from 00 to 99. With a little bit of work, we can make our regular expression more intelligent and only accept values from 01 to 12.

Let’s start with the months January (01) through September (09) which has leading zeros. In this case we will accept a leading zero followed by any digit from 1 to 9, like this.

/0[1-9]/Edit with Regexity

The 0 in front means we want a literal match for the 0 character, while the 1-9 in square brackets show that we’ll accept one number from 1 to 9.

For the months October (10) through December (12) which all start with 1, we have a slightly different setup. A 1 character can only be followed by either 0, 1, or 2. We indicate that like this:

/1[0-2]/Edit with Regexity

The 1 in front indicates a literal match for the 1 character, while the 0-2 in square brackets will accept one character from 0 to 2.

We can combine these two month-representations using an OR symbol (|) or pipe character, and enclose them in round brackets to indicate that they function together as a group.

/(0[1-9]|1[0-2])/Edit with Regexity

Adding this to our 4 digit year regex produces the following:

/\d{4}-(0[1-9]|1[0-2])/Edit with Regexity

This is then followed by another hyphen character (-):

/\d{4}-(0[1-9]|1[0-2])-/Edit with Regexity

And finally we can write the code that will accept a two digit day representation from 01 to 31, padded with leading zeros if necessary. Similar to the month representation, we’ll split the day up into groups.

First we’ll tackle days 01 through 09 which start with a 0. These can be followed by one digit from 1 to 9 (note that we don’t include 0 here since 00 is not a valid day representation):

/0[1-9]/Edit with Regexity

Next we’ll combine the days 10 through 19 and 20 through 29 by specifying that we can have either a 1 or a 2 followed by any one number from 0 to 9:

/[12][0-9]/Edit with Regexity

The [12] in square brackets indicate that we’ll accept either 1 or 2.

And for the dates 30 to 31, we require the number 3 followed by either a 0 or a 1.

/3[01]/Edit with Regexity

We can now combine these three day-representations into a group by enclosing them in round brackets and separating them by the OR character (|):

/(0[1-9]|[12][0-9]|3[01])/Edit with Regexity

And finally we can join them back with the rest of our expression:

/\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])/Edit with Regexity

To ensure that we only match the date and nothing else before or after it, we should include the start-of-string character ^Edit with Regexity and end-or-string character $Edit with Regexity at the start and end of the expression, respectively:

/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/Edit with Regexity

And there we have it! This regex code will accept the YYYY-MM-DD date format with some degree of intelligence.

Regular Expression for Basic Format Dates (YYYYMMDD)

ISO 8601 allows for date representations without the hyphens separating the parts of the date. For example 14 June 2021 can be written as 2021-06-14 in the extended human-readable format but also as 20210614 in the basic format.

To accept the basic format, we can use the following regular expression (which is the normal expression above but with the separating hyphens removed):

/^\d{4}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$/Edit with Regexity

Regular Expression for BC Dates (-YYYY-MM-DD)

The ISO 8601 international standard also allows for BC dates. These are specified by a leading minus sign Edit with Regexity and can also include more year characters (for example a 5-digit year formatted as YYYYY) if agreed upon previously by both the sender and receiver.

To include BC dates, we need to modify our existing data expression by including an optional sign (+ or -) at the front of the expression:

/^[+-]?\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/Edit with Regexity

The [+-]Edit with Regexity shows that we’ll accept either +Edit with Regexity or Edit with Regexity while the zero-or-one quantifier ?Edit with Regexity indicates that this characters is optional.

If agreed upon by both the sender and receiver, we can modify the year representation to include additional year characters. For example, if we’d like to include a 5th year-character (i.e. YYYYY) we can change the quantifier behind the year representation to {4,5}Edit with Regexity:

 /^[+-]?\d{4,5}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/Edit with Regexity

The {4,5}Edit with Regexity quantifier specifies that we’ll accept between 4 and 5 of any of the preceding digits.

If we’d like to include a 6th year-character (YYYYYY), we can change the quantifier to {4,6}Edit with Regexity, and so on.

Other Considerations

The regular expressions developed above have some degree of intelligence in that they only accept months between 01 and 12 and days between 01 and 31. However, they can’t rule out invalid dates like 2021-04-31 or 2021-02-30 (some months only have 30 days and February never has more than 29).

We can modify our original regular expression to only include valid dates as seen below. However, this uses a lot of OR symbols (|) and nested groups and easily become confusing to any programmer.

/^\d{4}-(02-(0[1-9]|[12][0-9])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))$/Edit with Regexity

In addition, this does not rule out invalid leap days. For example, it will match 2021-02-29 although 2021 is not a leap year. To do this check will require checking if the year is divisible by 4. This is not possible with regular expressions and it is recommended that you use the date tools in your programming language of choice to check for validity.

However, the regular expressions given present are a quick preliminary method of testing a date for a valid YYYY-MM-DD format and ruling out invalid dates before proceeding to more complex validation.

Sources

The regular expressions on this page were adapted from solutions posted on Stack Overflow by Vinod to this question. In addition, articles here and here proved helpful in determining the exact format of an appropriate ISO 8601 date.

Benjamin

Founder, owner, and sole content creator on RegexLand. Enjoys programming, blogging, and teaching others how to do the same. Read more...

7 thoughts on “Regex for Dates (YYYY-MM-DD)”

  1. Super helpful, thank you!
    I wondering how to make the year have constraints. Like only letting entries from 1800-present for example.

    Reply
    • This becomes tricky pretty fast, as it will be different for any date you pick as a constraint. You will need to hard-code year counts that appear after that year and before another year (i.e. the current year).

      For example, years between 1800 and 1999 can be validated using something like:

      /1[89]\d\d/

      Once you add anything in the 2000 range you will need to start using alternation (i.e. the pipe character |) to include those years. It’s not as easy as switching the 1 at the start of the expression above to [12] since that would also allow years like 2800. Hope you can see my point.

      It becomes easier to simply use regex to validate that the entry is indeed four numbers (i.e. a year count) and then use extra logic in your native programming language to parse a digit from the input and check that the digit falls within your year count constraints.

      Reply

Leave a Comment