Written by Paul Ogier on November 4, 2020

Advanced Date Matching with Regex - 2 methods to validate

Are you looking to do an advanced date matching and validation with Regular Expressions? Are you trying to make sure that your validation takes into account months with 30 and 31 days, and that pesky February who has 28 days and then every 4 years it has 29 days?!

Then this video below is for you!

Resources for Advanced Date Matching with Regex

https://regex101.com/r/9JIyQs/2 - Simple Example
https://www.debuggex.com/ - Regex Visualizer
https://jex.im/regulex/#!flags=&re=%5E(a%7Cb)*%3F%24 - Another Regex Visualizer
http://taming.tech/Google-Doc-Regex-Planning Thinking behind the Regex for a Date
https://regex101.com/r/YSSP9v/1 - Full Regex for date
https://regex101.com/r/YSSP9v/2 - Blank for you to start with
https://regex101.com/r/YSSP9v/3 - My completed example after demo

About this Regex Tutorial

We break down the complexity, of using 2 different examples of how to validate months using Regex, into bite size chunks. We go through the whole process from scratch and show you the thinking in how to formulate your expressions.

One of the difficulties with working on a new expression is being able to see where you have gone wrong, and then troubleshooting the expressions. Here we use a Regex Visualiser to show you how it works. How the validation goes from 1 side of the Regular Expression and gets thrown out of each leg and is only valid in one place.

In this tutorial we start with a simple example of how to validate against date entries. The problem with this Regex is that if you are looking to reject inputs that might be incorrect like 30/Feb/2001 then this will not stop those from being entered.

Simple Date Matching

(?<MM>[01]?[0-9])\/(?<DD>[0-3]?[0-9])\/(?<YYYY>[0-2][0-9][0-9][0-9])

If you look at the examples from this website https://www.regular-expressions.info/dates.html you will see that they start with this example.

^(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$

You can see their code working here https://regex101.com/r/9ec0Ko/1 This still doesn't validate for months working on different days per month or leap years. They do however continue on to validate the dates using Perl code. This is in the yyyy-mm-dd format from 1900-01-01 through 2099-12-31.

sub isvaliddate {
  my $input = shift;
  if ($input =~ m!^((?:19|20)\d\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$!) {
    # At this point, $1 holds the year, $2 the month and $3 the day of the date entered
    if ($3 == 31 and ($2 == 4 or $2 == 6 or $2 == 9 or $2 == 11)) {
      return 0; # 31st of a month with 30 days
    } elsif ($3 >= 30 and $2 == 2) {
      return 0; # February 30th or 31st
    } elsif ($2 == 2 and $3 == 29 and not ($1 % 4 == 0 and ($1 % 100 != 0 or $1 % 400 == 0))) {
      return 0; # February 29th outside a leap year
    } else {
      return 1; # Valid date
    }
  } else {
    return 0; # Not a date
  }
}

That is fine for their example, but I want to have my validation all in one line that can be adapted for Python, PHP, PowerShell etc. so this doesn't work for me.

So we start with a very complex looking expression. If you are happy to just copy and paste then you can use this. But if you want to learn how to do this, then please watch the video.

So we start with a very complex looking expression. If you are happy to just copy and paste then you can use this. But if you want to learn how to do this, then please watch the video.

^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)(?:0?2|(?:Feb))\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

Check it out here https://regex101.com/r/YSSP9v/1 We have used the UK and South African date format here of DD/MM/YYYY. You can obviously update this to the international date format of YYYY/MM/DD.

After going through the validation and showing you how to work and troubleshoot your validation, I finished on this expression, which I actually like more than my original example.

^(?<Monthswith31>31(?<s1>\/|\.|-)(0?[13578]|1[02]|(Jan|Mar|May|Jul|Aug|Oct|Dec))\g<s1>(?<Year>19[0-9][0-9]|200[0-7]))$|^(?<Monthswith29or30>(29|30)(?<s2>\/|\.|-)(0?[1,3-9]|1[0-2]|(Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\g<s2>(?<Year2>19[0-9][0-9]|200[0-7]))$|^(?<Allmonthswith28>(0?[1-9]|1[0-9]|2[0-8])(?<s3>\/|\.|-)(0?[1-9]|1[0-2]|(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\g<s3>(?<Year3>19[0-9][0-9]|200[0-7]))$|^(?<FebLeapYears>29(?<s4>\/|\.|-)(0?2|Feb)\g<s4>(19([02468][048]|[13579][26])|200[04]))$

You can play and test it for yourself here https://regex101.com/r/YSSP9v/3

We have worked with 4 "legs" in this Regex. One to validate all months with 31 days, so that would be Jan, Mar, May, Jul, Aug, Oct, Dec. Then we look at all months with 29 or 30 days, so that would be Jan, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec. Then all the months with 1-28 days so that is all months. Finally, we look at leap years that would have Feb 29 in years like 1996, 2000, 2004 etc. and we play with an easy way to figure out what the year would validate against.

This video is an hour long because I take you through my logic and thinking so that you can figure out any Regex that you would like to play with.

Udemy Regex Course

Taming REGEX

This tutorial is a part of a Regex course on Udemy. Join us using the Udemy Coupon code on that page to get the course for the best possible price.

Related Posts