Date Format (dd/mm/yyyy) Regular Expression Pattern
(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\\d\\d)
Description
( #start of group #1 0?[1-9] # 01-09 or 1-9 | # ..or [12][0-9] # 10-19 or 20-29 | # ..or 3[01] # 30, 31 ) #end of group #1 / # follow by a "/" ( # start of group #2 0?[1-9] # 01-09 or 1-9 | # ..or 1[012] # 10,11,12 ) # end of group #2 / # follow by a "/" ( # start of group #3 (19|20)\\d\\d # 19[0-9][0-9] or 20[0-9][0-9] ) # end of group #3
The above regular expression is used to validate the date format in “dd/mm/yyyy”, you can easy customize to suit your need. However, it’s a bit hard to validate the leap year , 30 or 31 days of a month, we may need basic logic as below.
P.S Do let me know, if you know the regular expression which can check the leap and days of a month.
Example in Java
package com.mkyong.regex; import java.util.regex.Matcher; import java.util.regex.Pattern; public class DateValidator{ private Pattern pattern; private Matcher matcher; private static final String DATE_PATTERN = "(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\\d\\d)"; public DateValidator(){ pattern = Pattern.compile(DATE_PATTERN); } /** * Validate date format with regular expression * @param date date address for validation * @return true valid date fromat, false invalid date format */ public boolean validate(final String date){ matcher = pattern.matcher(date); if(matcher.matches()){ matcher.reset(); if(matcher.find()){ String day = matcher.group(1); String month = matcher.group(2); int year = Integer.parseInt(matcher.group(3)); if (day.equals("31") && (month.equals("4") || month .equals("6") || month.equals("9") || month.equals("11") || month.equals("04") || month .equals("06") || month.equals("09"))) { return false; // only 1,3,5,7,8,10,12 has 31 days } else if (month.equals("2") || month.equals("02")) { if(year % 4==0){ //leap year if(day.equals("30") || day.equals("31")){ return false; }else{ return true; } }else{ if(day.equals("29")||day.equals("30")||day.equals("31")){ return false; }else{ return true; } } }else{ return true; } }else{ return false; } }else{ return false; } } }
Date format that match:
1) “1/1/2010″ , “01/01/2020″
2) “31/1/2010″, “31/01/2020″
3) “29/2/2008″, “29/02/2008″
4) “28/2/2009″, “28/02/2009″
5) “31/3/2010″, “31/03/2010″
6) “30/4/2010″, “30/04/2010″
7) “31/5/2010″, “31/05/2010″
8 ) “30/6/2010″, “30/06/2010″
9) “31/7/2010″, “31/07/2010″
10) “31/8/2010″, “31/08/2010″
11) “30/9/2010″, “30/09/2010″
12) “31/10/2010″, “31/10/2010″
13) “30/11/2010″, “30/11/2010″
14) “31/12/2010″, “31/12/2010″
Date format doesn’t match:
1) “32/1/2010″ , “32/01/2020″ – day is out of range [1-31]
2) “1/13/2010″, “01/01/1820″ – month is out of range [1-12], year is out of range [1900-2999]
3) “29/2/2007″, “29/02/2007″ – 2007 is not leap year, only has 28 days
4) “30/2/2008″, “31/02/2008″ – leap year in February has 29 days only
5) “29/a/2008″, “a/02/2008″ – month is invalie, day is invalid
6) “333/2/2008″, “29/02/200a” – day is invalid, year is invalid
7) “31/4/20100″, “31/04/2010″ – April has 30 days only
8 ) “31/6/2010″, “31/06/2010″ -June has 30 days only
9) “31/9/2010″, “31/09/2010″ – September has 30 days only
10) “31/11/2010″ – November has 30 days only
Unit Test – DateValidatorTest
package com.mkyong.regex; import org.testng.Assert; import org.testng.annotations.*; /** * Date format dd/mm/yyyy validator Testing * @author mkyong * */ public class DateValidatorTest { private DateValidator dateValidator; @BeforeClass public void initData(){ dateValidator = new DateValidator(); } @DataProvider public Object[][] ValidDateProvider() { return new Object[][]{ new Object[] {"1/1/2010"}, new Object[] {"01/01/2020"}, new Object[] {"31/1/2010"}, new Object[] {"31/01/2020"}, new Object[] {"29/2/2008"}, new Object[] {"29/02/2008"}, new Object[] {"28/2/2009"}, new Object[] {"28/02/2009"}, new Object[] {"31/3/2010"}, new Object[] {"31/03/2010"}, new Object[] {"30/4/2010"}, new Object[] {"30/04/2010"}, new Object[] {"31/5/2010"}, new Object[] {"31/05/2010"}, new Object[] {"30/6/2010"}, new Object[] {"30/06/2010"}, new Object[] {"31/7/2010"}, new Object[] {"31/07/2010"}, new Object[] {"31/8/2010"}, new Object[] {"31/08/2010"}, new Object[] {"30/9/2010"}, new Object[] {"30/09/2010"}, new Object[] {"31/10/2010"}, new Object[] {"31/10/2010"}, new Object[] {"30/11/2010"}, new Object[] {"30/11/2010"}, new Object[] {"31/12/2010"}, new Object[] {"31/12/2010"} }; } @DataProvider public Object[][] InvalidDateProvider() { return new Object[][]{ new Object[] {"32/1/2010"}, new Object[] {"32/01/2020"}, new Object[] {"1/13/2010"}, new Object[] {"01/01/1820"}, new Object[] {"29/2/2007"}, new Object[] {"29/02/2007"}, new Object[] {"30/2/2008"}, new Object[] {"31/02/2008"}, new Object[] {"29/a/2008"}, new Object[] {"a/02/2008"}, new Object[] {"333/2/2008"}, new Object[] {"29/02/200a"}, new Object[] {"31/4/2010"}, new Object[] {"31/04/2010"}, new Object[] {"31/6/2010"}, new Object[] {"31/06/2010"}, new Object[] {"31/9/2010"}, new Object[] {"31/09/2010"}, new Object[] {"31/11/2010"} }; } @Test(dataProvider = "ValidDateProvider") public void ValidDateTest(String date) { boolean valid = dateValidator.validate(date); System.out.println("Date is valid : " + date + " , " + valid); Assert.assertEquals(true, valid); } @Test(dataProvider = "InvalidDateProvider", dependsOnMethods="ValidDateTest") public void InValidDateTest(String date) { boolean valid = dateValidator.validate(date); System.out.println("Date is valid : " + date + " , " + valid); Assert.assertEquals(false, valid); } }
Unit Test – Result
[Parser] Running:
E:\workspace\mkyong\temp-testng-customsuite.xml
Date is valid : 1/1/2010 , true
Date is valid : 01/01/2020 , true
Date is valid : 31/1/2010 , true
Date is valid : 31/01/2020 , true
Date is valid : 29/2/2008 , true
Date is valid : 29/02/2008 , true
Date is valid : 28/2/2009 , true
Date is valid : 28/02/2009 , true
Date is valid : 31/3/2010 , true
Date is valid : 31/03/2010 , true
Date is valid : 30/4/2010 , true
Date is valid : 30/04/2010 , true
Date is valid : 31/5/2010 , true
Date is valid : 31/05/2010 , true
Date is valid : 30/6/2010 , true
Date is valid : 30/06/2010 , true
Date is valid : 31/7/2010 , true
Date is valid : 31/07/2010 , true
Date is valid : 31/8/2010 , true
Date is valid : 31/08/2010 , true
Date is valid : 30/9/2010 , true
Date is valid : 30/09/2010 , true
Date is valid : 31/10/2010 , true
Date is valid : 31/10/2010 , true
Date is valid : 30/11/2010 , true
Date is valid : 30/11/2010 , true
Date is valid : 31/12/2010 , true
Date is valid : 31/12/2010 , true
Date is valid : 32/1/2010 , false
Date is valid : 32/01/2020 , false
Date is valid : 1/13/2010 , false
Date is valid : 01/01/1820 , false
Date is valid : 29/2/2007 , false
Date is valid : 29/02/2007 , false
Date is valid : 30/2/2008 , false
Date is valid : 31/02/2008 , false
Date is valid : 29/a/2008 , false
Date is valid : a/02/2008 , false
Date is valid : 333/2/2008 , false
Date is valid : 29/02/200a , false
Date is valid : 31/4/2010 , false
Date is valid : 31/04/2010 , false
Date is valid : 31/6/2010 , false
Date is valid : 31/06/2010 , false
Date is valid : 31/9/2010 , false
Date is valid : 31/09/2010 , false
Date is valid : 31/11/2010 , false
PASSED: ValidDateTest("1/1/2010")
PASSED: ValidDateTest("01/01/2020")
PASSED: ValidDateTest("31/1/2010")
PASSED: ValidDateTest("31/01/2020")
PASSED: ValidDateTest("29/2/2008")
PASSED: ValidDateTest("29/02/2008")
PASSED: ValidDateTest("28/2/2009")
PASSED: ValidDateTest("28/02/2009")
PASSED: ValidDateTest("31/3/2010")
PASSED: ValidDateTest("31/03/2010")
PASSED: ValidDateTest("30/4/2010")
PASSED: ValidDateTest("30/04/2010")
PASSED: ValidDateTest("31/5/2010")
PASSED: ValidDateTest("31/05/2010")
PASSED: ValidDateTest("30/6/2010")
PASSED: ValidDateTest("30/06/2010")
PASSED: ValidDateTest("31/7/2010")
PASSED: ValidDateTest("31/07/2010")
PASSED: ValidDateTest("31/8/2010")
PASSED: ValidDateTest("31/08/2010")
PASSED: ValidDateTest("30/9/2010")
PASSED: ValidDateTest("30/09/2010")
PASSED: ValidDateTest("31/10/2010")
PASSED: ValidDateTest("31/10/2010")
PASSED: ValidDateTest("30/11/2010")
PASSED: ValidDateTest("30/11/2010")
PASSED: ValidDateTest("31/12/2010")
PASSED: ValidDateTest("31/12/2010")
PASSED: InValidDateTest("32/1/2010")
PASSED: InValidDateTest("32/01/2020")
PASSED: InValidDateTest("1/13/2010")
PASSED: InValidDateTest("01/01/1820")
PASSED: InValidDateTest("29/2/2007")
PASSED: InValidDateTest("29/02/2007")
PASSED: InValidDateTest("30/2/2008")
PASSED: InValidDateTest("31/02/2008")
PASSED: InValidDateTest("29/a/2008")
PASSED: InValidDateTest("a/02/2008")
PASSED: InValidDateTest("333/2/2008")
PASSED: InValidDateTest("29/02/200a")
PASSED: InValidDateTest("31/4/2010")
PASSED: InValidDateTest("31/04/2010")
PASSED: InValidDateTest("31/6/2010")
PASSED: InValidDateTest("31/06/2010")
PASSED: InValidDateTest("31/9/2010")
PASSED: InValidDateTest("31/09/2010")
PASSED: InValidDateTest("31/11/2010")
===============================================
com.mkyong.regex.DateValidatorTest
Tests run: 47, Failures: 0, Skips: 0
===============================================
===============================================
mkyong
Total tests run: 47, Failures: 0, Skips: 0
===============================================Want to learn more about regular expression? Highly recommend the best and classic book – “Mastering Regular Expression”



[...] ==> See the explanation and example here [...]
In a tutorial on regular expressions I wrote, at the end I show a RE validating dates, including leap years. Tutorial is: Regular Expressions: a simple, easy tutorial
I haven’t invented the RE itself, but I improved it a bit, shortening it.
As pointed out, often adding some procedural logic can goes a long way to avoid overly complex regexes… But hey, such exercise is a nice and entertaining way to improve the RE-fu…
Beside, sometime we find ourself in the need to feed a validation logic with REs, without a way to add code. So it might be interesting to know how to handle these cases.
That said, in an HTML forum having to validate dates, I used only a very primitive JavaScript RE, rejecting most blatant errors. I let the Java back-end to catch the corner cases and report them to the user…
it’s good to bring some discussion here
, this example is created purely for demonstrate about how to use RegEx way to validate the a date.
Not going to argue on the performance test, thanks for your contribution.
The book “Java Puzzla” has a very good topic on the date validation in SimpleDateFormat , use static initializer for the SimpleDateFormat is much better than new object everytime.
A little precision, you can use a strict parsing with SimpleDateFormat by using sdf.setLenient(false) ( http://java.sun.com/j2se/1.4.2/docs/api/java/text/DateFormat.html#setLenient%28boolean%29 ).
I ran all your tests above 10,000 times using your code and 10,000 times using my code. Both finished in 3 seconds.
Therefore, in 99.99999% of cases, my code is better than yours because it is effectively the same speed but much less code.
Prove it to me. Even if you validated a million dates, my way won’t take more than a second or two more than yours.
In a real business application no one will notice the difference in speed of a few milliseconds per check.
All that code to validate a date? You don’t need anywhere near that much:
public boolean dateValid(String d) {
SimpleDateFormat sdf = new SimpleDateFormat(“M/d/yyyy”);
try {
Date dx = sdf.parse(d);
return d.equals(sdf.format(dx));
} catch (ParseException e) {
return false;
}
}
Hi John,
Thanks for it, your method is much more easy and straight forward, and most of the time i’m using your way as well
.
However, the drawback is the new date object is expensive to create for validation, and RegEx is much more faster, if performance is one of your core consideration.