258

I need to validate a date string for the format dd/mm/yyyy with a regular expresssion.

This regex validates dd/mm/yyyy, but not the invalid dates like 31/02/4500:

^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$

What is a valid regex to validate dd/mm/yyyy format with leap year support?

SeaBean
  • 22,547
  • 3
  • 13
  • 25
Nalaka526
  • 11,278
  • 21
  • 82
  • 116
  • 3
    I think it might help if you set an accurate expectation, as this regex does NOT, in fact, correctly validate leap years; e.g., there is no Feb. 29th in 2013, but this regex asserts that such is valid: http://regexr.com?346fp – TML Mar 19 '13 at 15:55
  • 11
    Why with Regex? There are easier (and more accurate) ways... – Dan Puzey Mar 19 '13 at 15:55
  • @TML You are correct... I have mistaken, It DOES NOT validate the leap year correctly... :( – Nalaka526 Mar 19 '13 at 16:00
  • 1
    @DanPuzey I'm not that familiar with Javascript, just wanted to get it validated client side... Can you suggest something more reliable and easier to maintain? – Nalaka526 Mar 19 '13 at 16:03
  • 3
    Regular expressions are for matching patterns, not checking numeric values. Find a likely string with the regex, then check its numeric value in whatever your host language is (PHP, whatever). – Andy Lester Mar 19 '13 at 16:41
  • 1
    You are using the wrong tool. regular expressions are not for validating. – Burhan Khalid Mar 21 '14 at 09:27
  • 3
    This answer has been added to the [Stack Overflow Regular Expression FAQ](http://stackoverflow.com/a/22944075/2736496), under "Common Validation Tasks". – aliteralmind Apr 10 '14 at 01:23
  • 4
    @BurhanKhalid: You are wrong. Regular expression is the best tool for validation, since HTML5 input has an attribute named `pattern` that takes a regular expression, and the browsers validate automatically against the regex without use of any javascript at all. Just by settting a regex in the pattern attribute! – awe Apr 15 '14 at 06:43
  • 2
    True dat, @DanPuzey. Any reason the original poster couldn't use something like moment#isValid? (If you haven't heard of momentjs, get on the good train: http://momentjs.com/docs/) – siege_Perilous Jun 09 '14 at 23:01
  • 1
    The linked dupe reason was for Ruby, thus, reopening this generic regex post. – Wiktor Stribiżew Feb 22 '19 at 12:15
  • Renaming the title to update the set of date formats currently supported in order to facilitate searching by format for future viewers. – SeaBean Sep 30 '21 at 07:41

28 Answers28

439

The regex you pasted does not validate leap years correctly, but there is one that does in the same post. I modified it to take dd/mm/yyyy, dd-mm-yyyy or dd.mm.yyyy.

^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\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])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

I tested it a bit in the link Arun provided in his answer and also here and it seems to work.

Edit February 14th 2019: I've removed a comma that was in the regex which allowed dates like 29-0,-11

J. Meijer
  • 21
  • 7
Ofir Luzon
  • 10,635
  • 3
  • 41
  • 52
  • I want to add 2 English Alphabets before date validation So my correct output would be "AB 12/12/1999", could anyone help me write regex for that ? my current regex is : /[a-zA-Z ](0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d/ but it allows more than 4 char in year – Salman Nausher Jul 26 '22 at 11:18
  • it works almost perfectly! Instead of "d{2}", you have to put "d{4}" if you want the date in the format dd/mm/yyyy. Otherwise, it'll work for the format dd/mm/yy. – ladytoky0 Aug 04 '23 at 13:27
304

I have extended the regex given by @Ofir Luzon for the formats dd-mmm-YYYY, dd/mmm/YYYY, dd.mmm.YYYY as per my requirement. Anyone else with same requirement can refer this

^(?:(?: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})$

and tested for some test cases here https://regexr.com/39tr1.

For better understanding for this Regular expression refer this image:

enter image description here

Edit

Extending it for yyyy/mm/dd, yyyy-mm-dd or yyyy.mm.dd some test cases https://regex101.com/r/3TZfyU/1

^(?:(?:1[6-9]|[2-9]\d)?\d{2})(?:(?:(\/|-|\.)(?:0?[13578]|1[02])\1(?:31))|(?:(\/|-|\.)(?:0?[13-9]|1[0-2])\2(?:29|30)))$|
^(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\/|-|\.)0?2\3(?:29)$|
^(?:(?:1[6-9]|[2-9]\d)?\d{2})(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:0?[1-9]|1\d|2[0-8])$
halfer
  • 19,824
  • 17
  • 99
  • 186
Alok Chaudhary
  • 3,481
  • 1
  • 16
  • 19
83

Notice:

Your regexp does not work for years that "are multiples of 4 and 100, but not of 400". Years that pass that test are not leap years. For example: 1900, 2100, 2200, 2300, 2500, etc. In other words, it puts all years with the format \d\d00 in the same class of leap years, which is incorrect. – MuchToLearn

So it works properly only for [1901 - 2099] (Whew)

dd/MM/yyyy:

Checks if leap year. Years from 1900 to 9999 are valid. Only dd/MM/yyyy

(^(((0[1-9]|1[0-9]|2[0-8])[\/](0[1-9]|1[012]))|((29|30|31)[\/](0[13578]|1[02]))|((29|30)[\/](0[4,6,9]|11)))[\/](19|[2-9][0-9])\d\d$)|(^29[\/]02[\/](19|[2-9][0-9])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)$)
gdZeus
  • 1,081
  • 8
  • 7
  • To make it quicker you can make the groups non-grouping. http://regexr.com/3esom ^(?:(?:(?:(?:0[1-9]|1[0-9]|2[0-8])[\/](?:0[1-9]|1[012]))|(?:(?:29|30|31)[\/](?:0[13578]|1[02]))|(?:(?:29|30)[\/](?:0[4,6,9]|11)))[\/](?:19|[2-9][0-9])\d\d)|(?:29[\/]02[\/](?:19|[2-9][0-9])(?:00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96))$ – Matt Vukomanovic Dec 15 '16 at 09:17
36

try this.

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

you can test regular expression at http://www.regular-expressions.info/javascriptexample.html easily.

A.T.
  • 24,694
  • 8
  • 47
  • 65
  • 2
    this regex does not validate the date with the month like `30-02-2001`... anyway thanks for the answer :) – Nalaka526 Mar 19 '13 at 15:16
  • @Nalaka526, this regex works well enough for basic date validation. If you want to reliably check the validity of a date, you can always parse it. – Peter Chaula Jan 22 '21 at 06:17
  • I guess `[- /.]` was meant to be `[- /\.]`, where the dot is a actual character, not "any char" – Random Jul 28 '22 at 09:49
  • I tried this with dart&flutter and found it worked when I selected the date with the datetimepicker and printed it. I am grateful to you, sir. – Abdullah T Dec 23 '22 at 11:26
17

For those who look at these and get completely confused, here is an excerpt from my script. Unfortunately, all it does is match valid numbers in a date time input, and 31st Feb will be marked as valid, but as so many have said, regex really isn't the best tool to do this test.

To match a date in the format 'yyyy-MM-dd hh:mm' (Or indeed in whatever order you please)

var dateerrors = false;
var yearReg = '(201[4-9]|202[0-9])';            ///< Allows a number between 2014 and 2029
var monthReg = '(0[1-9]|1[0-2])';               ///< Allows a number between 00 and 12
var dayReg = '(0[1-9]|1[0-9]|2[0-9]|3[0-1])';   ///< Allows a number between 00 and 31
var hourReg = '([0-1][0-9]|2[0-3])';            ///< Allows a number between 00 and 24
var minReg = '([0-5][0-9])';                    ///< Allows a number between 00 and 59
var reg = new RegExp('^' + yearReg + '-' + monthReg + '-' + dayReg + ' ' + hourReg + ':' + minReg + '$', 'g');
$('input').filter(function () {return this.id.match(/myhtml_element_with_id_\d+_datetime/);}).each(function (e) {
    if (e > 0) {
        // Don't test the first input. This will use the default
        var val = $(this).val();
        if (val && !val.trim().match(reg)) {
            dateerrors = true;
            return false;
        }
    }
});
if (dateerrors) {
    alert('You must enter a validate date in the format "yyyy-mm-dd HH:MM", e.g. 2019-12-31 19:30');
    return false;
}

The above script starts off by building a regex object. It then finds all of the inputs whose id's match a certain pattern and then loops through these. I don't test the first input I find (if (e > 0)).

A bit of explanation:

var reg = new RegExp('^' + yearReg + '-' + monthReg + '-' + dayReg + ' ' + hourReg + ':' + minReg + '$', 'g');

^ means start of match, whereas $ means end of match.

return this.id.match(/myhtml_element_with_id_\d+_datetime/);

\d+ means match a single or a contiguous sequence of integers, so myhtml_element_with_id_56_datetime and myhtml_element_with_id_2_datetime will match, but myhtml_element_with_id_5a_datetime will not

Luke Madhanga
  • 6,871
  • 2
  • 43
  • 47
13

I suspect that the following is as accurate as can be expected without knowing when the user's locale switched over from the Julian to the Gregorian calendars.

It accepts either '-', '/', or nothing as separators between year, month, and day, no matter the order.

MMddyyyy:

^(((0[13-9]|1[012])[-/]?(0[1-9]|[12][0-9]|30)|(0[13578]|1[02])[-/]?31|02[-/]?(0[1-9]|1[0-9]|2[0-8]))[-/]?[0-9]{4}|02[-/]?29[-/]?([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048]|0[0-9]|1[0-6])00))$

ddMMyyyy:

^(((0[1-9]|[12][0-9]|30)[-/]?(0[13-9]|1[012])|31[-/]?(0[13578]|1[02])|(0[1-9]|1[0-9]|2[0-8])[-/]?02)[-/]?[0-9]{4}|29[-/]?02[-/]?([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048]|0[0-9]|1[0-6])00))$

yyyyMMdd:

^([0-9]{4}[-/]?((0[13-9]|1[012])[-/]?(0[1-9]|[12][0-9]|30)|(0[13578]|1[02])[-/]?31|02[-/]?(0[1-9]|1[0-9]|2[0-8]))|([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048]|0[0-9]|1[0-6])00)[-/]?02[-/]?29)$

Other than order, these all are accurate to the Julian Calendar (leap year every four years) until 1700, when the Gregorian Calendar diverges from the Julian. It has two issues:

  1. It accepts the year 0000, which doesn't exist in many, but not all, standards. Note that ISO 8601 does accept year 0000 (equivalent to 1 BCE).
  2. It doesn't skip the 10-13 days which were lost when the Gregorian Calendar came into use. This varies by locality though. For example, the Roman Catholic Church skipped 10 days, October 5th through October 14th, 1582, but Greece (the last to switch) skipped February 16th through the 28th of 1923, 13 days, having to take into account the leap years of 1700, 1800, and 1900.

This has been tested against Java's calendar implementation from the year 0001 until the year 9999 with the only discrepancy being the abovementioned 10 days in 1582.

Jason Greanya
  • 189
  • 1
  • 4
  • The YYYYMMDD regex incorrectly matches [01|02|03|05|06|07|09|10|11|13|14|15]00-02-29. Corrected regex: `^([0-9]{4}[-/]?((0[13-9]|1[012])[-/]?(0[1-9]|[12][0-9]|30)|(0[13578]|1[02])[-/]?31|02[-/]?(0[1-9]|1[0-9]|2[0-8]))|([0-9]{2}(([2468][048]|[02468][48])|[13579][26])|([13579][26]|[02468][048])00)[-/]?02[-/]?29)$`. Tested with python: https://repl.it/repls/DependentBestChapters – apnkpr Jan 16 '19 at 16:32
  • Note that Saudi Arabia recently (a year ago) switched from the Islamic Calendar, but Greece is still the last to switch from Julian to Gregorian. – Raymo111 Sep 26 '20 at 02:29
9
year  = ((20[012]\d|19\d\d)|(1\d|2[0123])) 
month = ((0[0-9])|(1[012]))
day   = ((0[1-9])|([12][0-9])|(3[01]))

year-month-day = (((20[012]\d|19\d\d)|(1\d|2[0123]))-((0[0-9])|(1[012]))-((0[1-9])|([12][0-9])|(3[01])))
day-month-year = (((0[1-9])|([12][0-9])|(3[01]))-((0[0-9])|(1[012]))-((20[012]\d|19\d\d)|(1\d|2[0123])))
year/month/day = (((20[012]\d|19\d\d)|(1\d|2[0123]))\/((0[0-9])|(1[012]))\/((0[1-9])|([12][0-9])|(3[01])))
month/day/year = (((0[0-9])|(1[012]))\/((0[1-9])|([12][0-9])|(3[01]))\/((20[012]\d|19\d\d)|(1\d|2[0123])))
day/month/year = (((0[1-9])|([12][0-9])|(3[01]))\/((0[0-9])|(1[012]))\/((20[012]\d|19\d\d)|(1\d|2[0123])))
day.month.year = (((0[1-9])|([12][0-9])|(3[01]))\.((0[0-9])|(1[012]))\.((20[012]\d|19\d\d)|(1\d|2[0123])))
year.month.day = (((20[012]\d|19\d\d)|(1\d|2[0123]))\.((0[0-9])|(1[012]))\.((0[1-9])|([12][0-9])|(3[01])))


all = (((20[012]\d|19\d\d)|(1\d|2[0123]))-((0[0-9])|(1[012]))-((0[1-9])|([12][0-9])|(3[01])))|(((0[1-9])|([12][0-9])|(3[01]))-((0[0-9])|(1[012]))-((20[012]\d|19\d\d)|(1\d|2[0123])))|(((20[012]\d|19\d\d)|(1\d|2[0123]))\/((0[0-9])|(1[012]))\/((0[1-9])|([12][0-9])|(3[01])))|(((0[0-9])|(1[012]))\/((0[1-9])|([12][0-9])|(3[01]))\/((20[012]\d|19\d\d)|(1\d|2[0123])))|(((0[1-9])|([12][0-9])|(3[01]))\/((0[0-9])|(1[012]))\/((20[012]\d|19\d\d)|(1\d|2[0123])))|(((0[1-9])|([12][0-9])|(3[01]))\.((0[0-9])|(1[012]))\.((20[012]\d|19\d\d)|(1\d|2[0123])))|(((20[012]\d|19\d\d)|(1\d|2[0123]))\.((0[0-9])|(1[012]))\.((0[1-9])|([12][0-9])|(3[01])))

its work for

yyyy-mm-dd
dd-mm-yyyy
yyyy/mm/dd
mm/dd/yyyy
dd/mm/yyyy
dd.mm.yyyy
yyyy.mm.dd

yy-mm-dd
dd-mm-yy
yyyy/mm/dd
mm/dd/yy
dd/mm/yy
dd.mm.yy
yy.mm.dd

but not work for where day = d or month = m, example d.m.yyyy

all example - enter link description here

  • 1
    May I ask how to achieve for ```day = d or month = m``` situations? – RoyRao Apr 20 '22 at 07:18
  • Hi @RoyRao, I faced the same problem. That's why I wrote what not work. I solved this problem with replace before regex. If you find a more elegant solution please let me know. – Алексей Коробов Apr 23 '22 at 10:27
  • I found one [here](https://ihateregex.io/expr/date/) that accepts single month or single day, but in fixed order of **day->month->year**. I'm not familiar with regex, maybe you can take a look? Thanks! – RoyRao Apr 24 '22 at 10:46
8

Here is another version of regex to match any of the following date formats and allow leading zeros to be omitted:

Regex: ^[0-3]?[0-9].[0-3]?[0-9].(?:[0-9]{2})?[0-9]{2}$

Matches:

1/1/11 or 1.1.11 or 1-1-11 : true 01/01/11 or 01.01.11 or 01-01-11 : true 01/01/2011 or 01.01.2011 or 01-01-2011 : true 01/1/2011 or 01.1.2011 or 01-1-2011 : true 1/11/2011 or 1.11.2011 or 1-11-2011 : true 1/11/11 or 1.11.11 or 1-11-11 : true 11/1/11 or 11.1.11 or 11-1-11 : true

Regular expression visualization

Debuggex Demo

Simple-Solution
  • 4,209
  • 12
  • 47
  • 66
7

Here I wrote one for dd/mm/yyyy where separator can be one of -.,/ year range 0000-9999.

It deals with leap years and is designed for regex flavors, that support lookaheads, capturing groups and backreferences. NOT valid for such as d/m/yyyy. If needed add further separators to [-.,/]

^(?=\d{2}([-.,\/])\d{2}\1\d{4}$)(?:0[1-9]|1\d|[2][0-8]|29(?!.02.(?!(?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)\d{2}(?:[02468][048]|[13579][26])))|30(?!.02)|31(?=.(?:0[13578]|10|12))).(?:0[1-9]|1[012]).\d{4}$

Test at regex101; as a Java string:

"^(?=\\d{2}([-.,\\/])\\d{2}\\1\\d{4}$)(?:0[1-9]|1\\d|[2][0-8]|29(?!.02.(?!(?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)\\d{2}(?:[02468][048]|[13579][26])))|30(?!.02)|31(?=.(?:0[13578]|10|12))).(?:0[1-9]|1[012]).\\d{4}$"

explained:

(?x) # modifier x: free spacing mode (for comments)
     # verify date dd/mm/yyyy; possible separators: -.,/
     # valid year range: 0000-9999

^    # start anchor

# precheck xx-xx-xxxx,... add new separators here
(?=\d{2}([-.,\/])\d{2}\1\d{4}$)

(?:  # day-check: non caturing group

  # days 01-28
  0[1-9]|1\d|[2][0-8]| 

  # february 29d check for leap year: all 4y / 00 years: only each 400
  # 0400,0800,1200,1600,2000,...
  29
  (?!.02. # not if feb: if not ...
    (?!
      # 00 years: exclude !0 %400 years
      (?!(?:[02468][1-35-79]|[13579][0-13-57-9])00)

      # 00,04,08,12,... 
      \d{2}(?:[02468][048]|[13579][26])
    )
  )|

  # d30 negative lookahead: february cannot have 30 days
  30(?!.02)|

  # d31 positive lookahead: month up to 31 days
  31(?=.(?:0[13578]|10|12))

) # eof day-check

# month 01-12
.(?:0[1-9]|1[012])

# year 0000-9999
.\d{4}

$ # end anchor

Also see SO Regex FAQ; Please let me know, if it fails.

Community
  • 1
  • 1
Jonny 5
  • 12,171
  • 2
  • 25
  • 42
6

Found this reg ex here

^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$

This validates the format mm/dd/yyyy and valid dates correctly (but not m/d/yyyy).

Some tests

Nalaka526
  • 11,278
  • 21
  • 82
  • 116
  • 1
    Only works before 2014, have to change |20(0[0-9]|1[0-4]))) to |20(0[0-9]|1[0-9]))) to support until 2019 – Tony Dong Sep 26 '16 at 22:19
5
"^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((19|20)\\d\\d)$"

will validate any date between 1900-2099

Tushar Gupta - curioustushar
  • 58,085
  • 24
  • 103
  • 107
user3575114
  • 993
  • 7
  • 13
5

The following expression is nice and easy to manipulate:

((((0[13578]|1[02])(\/|-|.)(0[1-9]|1[0-9]|2[0-9]|3[01]))|((0[469]|11)(\/|-|.)(0[1-9]|1[0-9]|2[0-9]|3[0]))|((02)((\/|-|.)(0[1-9]|1[0-9]|2[0-8]))))(\/|-|.)(19([6-9][0-9])|20(0[0-9]|1[0-4])))|((02)(\/|-|.)(29)(\/|-|.)(19(6[048]|7[26]|8[048]|9[26])|20(0[048]|1[26])))

It validates according to the MM/dd/YYYY format and allows for leap year support from 1960 to 2016. If you need the leap year support extended you need only manipulate this part of the expression:

(19(6[048]|7[26]|8[048]|9[26])|20(0[048]|1[26]))

Hope this helped you a lot

Chase
  • 51
  • 1
  • 1
3

Another answer which validates day (dd) depending upon the month (mm) and the year (yyyy) (i.e., also validates 29th Feb in leap years) and allows years ranging from 0001 to 9999 (0000 in a invalid year according to the Gregorian calendar)

^(?:(?:(?:0[1-9]|[12]\d|3[01])/(?:0[13578]|1[02])|(?:0[1-9]|[12]\d|30)/(?:0[469]|11)|(?:0[1-9]|1\d|2[0-8])/02)/(?!0000)\d{4}|(?:(?:0[1-9]|[12]\d)/02/(?:(?!0000)(?:[02468][048]|[13579][26])00|(?!..00)\d{2}(?:[02468][048]|[13579][26]))))$
Debanshu Kundu
  • 785
  • 7
  • 18
2

The best way according to me is to use the Moment.js isValid() method by specifying the format and use strict parsing.

As moment.js documentation says

As of version 2.3.0, you may specify a boolean for the last argument to make Moment use strict parsing. Strict parsing requires that the format and input match exactly, including delimiters.

value = '2020-05-25';
format = 'YYYY-MM-DD';
moment(value, format, true).isValid() // true
Sachin Kumar
  • 3,001
  • 1
  • 22
  • 47
2

Further extended the regex given by @AlokChaudhary to support:

1. dd mmm YYYY (in addition to dd-mmm-YYYY, dd/mmm/YYYY, dd.mmm.YYYY).

2. mmm in all CAPITAL LETTERS format (in addition to Title format)

dd mmm YYYY e.g. 30 Apr 2026 or 24 DEC 2028 are popular.

Extended regex:

(^(?:(?:(?:31(?:(?:([-.\/])(?:0?[13578]|1[02])\1)|(?:([-.\/ ])(?:Jan|JAN|Mar|MAR|May|MAY|Jul|JUL|Aug|AUG|Oct|OCT|Dec|DEC)\2)))|(?:(?:29|30)(?:(?:([-.\/])(?:0?[13-9]|1[0-2])\3)|(?:([-.\/ ])(?:Jan|JAN|Mar|MAR|Apr|APR|May|MAY|Jun|JUN|Jul|JUL|Aug|AUG|Sep|SEP|Oct|OCT|Nov|NOV|Dec|DEC)\4))))(?:(?:1[6-9]|[2-9]\d)?\d{2}))$|^(?:29(?:(?:([-.\/])(?:0?2)\5)|(?:([-.\/ ])(?:Feb|FEB)\6))(?:(?: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]|(?:1[0-2])))\7)|(?:([-.\/ ])(?:Jan|JAN|Feb|FEB|Mar|MAR|May|MAY|Jul|JUL|Aug|AUG|Oct|OCT|Dec|DEC)\8))(?:(?:1[6-9]|[2-9]\d)?\d{2}))$)

Test cases included in the Regex Demo

Features (retained):

  • Leap year checking (Feb 29 validation) includes the logics: (divisible by 4 but not divisible by 100) or (divisible by 400)
  • Supports years 1600 ~ 9999
  • Supports dd/mm/YYYY, dd-mm-YYYY, dd.mm.YYYY (but not dd mm YYYY)
  • Supports dd mmm YYYY, dd-mmm-YYYY, dd/mmm/YYYY, dd.mmm.YYYY (dd mmm YYYY newly added. mmm can be in CAPITAL e.g. DEC or Title format e.g. Dec)

Some additional minor touch-up as follows:

  1. Included the fix by Ofir Luzon on February 14th 2019 to remove a comma that was in the regex which allowed dates like 29-0,-11 [error replicated to Alok Chaudhary's regex]

  2. Replaced (\/|-|\.) by ([-.\/]) to minimize the use of backslash. \/ is still used in order to support some regex flavor e.g. PCRE(PHP) although some other regex flavor e.g. Python can simply use / inside the character class [ ]

  3. Added a pair of parenthesis () surrounding the whole regex to make it a capturing group for the whole matching string. This is useful for people using findAll type of functions to get a matching item list (e.g. re.findall in Python). This enable us to capture all the matching strings within a mult-line string with the following codes:

re.findall sample codes:

match_list = re.findall(regex, source_string)
for item in match_list:
    print(item[0])

Extended regex image: enter image description here

Credits should go to Ofir Luzon and Alok Chaudhary who created such excellent regexes for us all!

SeaBean
  • 22,547
  • 3
  • 13
  • 25
1

I'm working with an API that only accepts MM/DD/YYYY format. I couldn't find any other post that did leap years quite as well as Ofir's answer, so I tweaked it and am re-posting it here for anyone that might need it.

/^(?:(?:(?:0[13578]|1[02])(\/)31)\1|(?:(?:0[1,3-9]|1[0-2])(\/)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:02(\/)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\/)(?:0[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/
Community
  • 1
  • 1
Daniel
  • 1,789
  • 17
  • 15
1

In case you are looking for specific format, This works fine for "dd/MM/yyyy" & "dd/MMM/yyyy" date format only based on Alok answer.

function isValidDateFormatDDMMYYYY(inputDate) {
    var date_regex = /^(?:(?: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})$/;
    return date_regex.test(inputDate);
}

Few examples working thru this code -

  • isValidDateFormatDDMMYYYY("15/01/1999") // returns True
  • isValidDateFormatDDMMYYYY("15/Jan/1999") // returns True
  • isValidDateFormatDDMMYYYY("15/1/1999") // returns True
  • isValidDateFormatDDMMYYYY("1/15/1999") // returns False

Thanks

Rahul
  • 1,063
  • 2
  • 11
  • 22
1
import re
expression = "Nov 05 20:10:09 2020"
reg_ex = r'((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([0-2][0-9]|(3)[0-1]) (([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])) (\d{4}))'
assert re.fullmatch(reg_ex, expression), True

Expaination with respect to given Example

  • Nov = A group of possible months i.e. (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
  • 05 = A group of valid days i.e. ([0-2][0-9]|(3)[0-1])
  • 20:10:09 = A group for getting valid Hours : ([0-1][0-9]|2[0-3]), Minutes : ([0-5][0-9]) and Seconds : ([0-5][0-9])
  • 2020 = A group for getting year i.e (\d{4}))
Shivam Bharadwaj
  • 1,864
  • 21
  • 23
  • 2
    While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply – SherylHohman Nov 06 '20 at 19:44
  • @PareshMangukiya: There is not a link in this answer. – Jeremy Caney Nov 06 '20 at 21:18
1

Please Following Expression

Regex regex = new Regex(@"(((0|1)[0-9]|2[0-9]|3[0-1])\/(0[1-9]|1[0-2])\/((19|20)\d\d))$");
Ghotekar Rahul
  • 322
  • 3
  • 10
1

simple function for python

def is_valid_date(date_text):
    pattern = re.compile('\d{4}-\d{2}-\d{2}$')
    return pattern.match(date_text)
Kas
  • 313
  • 1
  • 14
0
((((0[13578]|1[02])\/(0[1-9]|1[0-9]|2[0-9]|3[01]))|((0[469]|11)\/(0[1-9]|1[0-9]|2[0-9]|3[0]))|((02)(\/(0[1-9]|1[0-9]|2[0-8]))))\/(19([6-9][0-9])|20([0-9][0-9])))|((02)\/(29)\/(19(6[048]|7[26]|8[048]|9[26])|20(0[048]|1[26]|2[048])))

will validate MM/DD/YYYY format with 1960 to 2028

if you need to extend leap year support then add

19(6[048]|7[26]|8[048]|9[26])|20(0[048]|1[26]|2[048]|3[26]|4[048])))

this is also work

^((((0[13578]|1[02])[/](0[1-9]|1[0-9]|2[0-9]|3[01]))|((0[469]|11)[/](0[1-9]|1[0-9]|2[0-9]|3[0]))|((02)([/](0[1-9]|1[0-9]|2[0-8]))))[/](19([6-9][0-9])|20([0-9][0-9])))|((02)[/](29)[/](19(6[048]|7[26]|8[048]|9[26])|20(0[048]|1[26]|2[048])))

if you can change format mm-dd-yyyy than replace [/] to [-] also check online http://regexr.com/

techspider
  • 3,370
  • 13
  • 37
  • 61
mahesh
  • 7
  • 2
0

For date MM/DD/YYYY you can use

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

It verify proper days and moths.

Remeber that you can check your regular expression at

regex101

which i recommend :)

Have fun!

0
^(((([13578]|0[13578]|1[02])[-](0[1-9]|[1-9]|1[0-9]|2[0-9]|3[01]))|(([469]|0[469]|11)[-]([1-9]|1[0-9]|2[0-9]|3[0]))|((2|02)([-](0[1-9]|1[0-9]|2[0-8]))))[-](19([6-9][0-9])|20([0-9][0-9])))|((02)[-](29)[-](19(6[048]|7[26]|8[048]|9[26])|20(0[048]|1[26]|2[048])))

this regex will validate dates in format:

12-30-2016 (mm-dd-yyyy) or 12-3-2016 (mm-d-yyyy) or 1-3-2016 (m-d-yyyy) or 1-30-2016 (m-dd-yyyy)

mexekanez
  • 266
  • 3
  • 7
0

I know it is a tangential answer to the question, but if the intention of the question is 'how do I validate a date?', then why not try letting the programming language do all the hard work (if you are using a language that can)?

e.g. in php

$this_date_object = date_create($this_date);

if ($this_date_object == false )
    {

        // process the error

    }
0

For use only for the day:

<input placeholder="day" maxlength="2" minlength="1" formControlName="birthDay" 
   name="birthDay"pattern="(0[1-9]|1[0-9]|2[0-9]|3[0-1])" >/

For use only for the month:

 <input placeholder="month" maxlength="2" minlength="1" 
  formControlName="month" name="month" formControlName="month" name="month" pattern="(0[1- 
  9]|1[0-2])">/
Fahimeh Ahmadi
  • 813
  • 8
  • 13
0

I know it's been a long time since this was answered, but maybe this could help someone else. The thing is that i wanted to, also, check the year, and let some past years match too. This regex match dates formated as "DD-MM-YYYY". So this function will return a regex:

const check_year = "01-01-2021"

console.log(get_regex())
console.log(check_year.match(get_regex()))

function get_regex(){
    let actual_year = `${new Date().getFullYear()}`
    let regex = new RegExp()
    let split_year = actual_year.split("")
    let year_regex = `${split_year[0]}[0-${split_year[1]}][0-${split_year[2]}][0-${split_year[3]}]$`
    let day_month_regex = "^([1-2][0-9]|3[0-1]|0?[1-9])[-]([1][0-2]|0?[1-9])[-]"

    regex.compile(day_month_regex+year_regex, "g")

    return regex
}
Matias Coco
  • 351
  • 3
  • 9
0

This is a regex to match strings of the date format, YYYY-MM-DD, with different kind of separators. It matches a string even in sentences, and with dates ending with st, nd and others.

As an example, it matches the date in these sentences:

"Her birthday is 2022 February 23rd; I will present here a gift."

"Her birthday is 2022 Feb 23rd; I will present here a gift."

"Her birthday is 2022 02 23; I will present here a gift."

This is the date regex:

"\b
(?<YYYY>[0-9]{4})
(?<!0000)(?<sep>[ /.,-])
(?|
    (?:(?<MM>0[13578]|1[02]|Jan(?:uary)?|Mar(?:ch)?|May|Jul(?:y)?|Aug(?:ust)?|Oct(?:ober)?|Dec(?:ember)?)\g{sep}(?<DD>0[1-9]|[12][0-9]|3[01]))|
    (?:(?<MM>0[469]|11|Apr(?:il)?|Jun(?:e)?|Sep(?:tember)?|Nov(?:ember)?)\g{sep}(?<DD>0[1-9]|[12][0-9]|30))|
    (?:(?<MM>02|Feb(?:ruary)?)\g{sep}(?<DD>0[1-9]|[12][0-9]))
)
(?:
    (?<=[023][1])st|
    (?<=[02][2])nd|
    (?<=[02][3])rd|
    (?<=(?:0[4-9])|(?:1[0-9])|20|(?:2[4-9])|30)th
)?
[,;.]?
\b"x

IMHO, it makes no sense for a regex to check for a leap year, when a simple, clear and understandable one-liner can be written for it:

is_leap_year(y) = ((y%4 == 0) && (y%100 != 0)) || (y%400 == 0)

Regexes are for matching strings and not for carrying out calculation. The best way is to match the string and then pass the captured MM group to the is_leap_year function if its 02, Feb or February, to validate the string.

0
/(0[1-9]|[12][0-9]|3[01])\-(0[1-9]|1[0,1,2])\-(19|20)\d{2}/

try this regex it will work ---> 24-02-1992

Example link https://regex101.com/r/6BmyWe/1

pgksunilkumar
  • 236
  • 2
  • 5