How to validate date with regular expression

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.

Java Regular Expression Example


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")) {
                  //leap year
		  if(year % 4==0){
			  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 invalid, 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 this best and classic book – “Mastering Regular Expression”



About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter, or befriend him on Facebook or Google Plus. If you like my tutorials, consider make a donation to these charities.

Comments

Leave a Reply

avatar
newest oldest most voted
Anh Minh
Guest
Anh Minh

So very nice, Tks very mich

criptik
Guest
criptik

I like your commented version of the regular expression but why not just compile that using Pattern.compile(DATE_PATTERN, Pattern.COMMENTS); ?
instead of rewriting it?

Ralf
Guest
Ralf

Hi,

I read this article and wanted to try and write a regex including 30/31 days and leap years (according to the article Despot posted). Haven’t tested the speed of it, but wanted to share the regex anyways.

It’s testing dates in format DD-MM-YYYY (so from 01-01-0000 to 31-12-9999).

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

Despot
Guest
Despot

Hey Ralf,

how about this one for the same purpose:
^(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-(([^01][0-9]|19|[2-9][0-9])\d\d)$
?

Cheers,
Despot

Ralf
Guest
Ralf

Hey Despot,

Wouldn’t your regex validate 31-04-2013 or 29-02-1900? My regex also checks if a month has 28, 29, 30 or 31 days.

Greets, Ralf

Despot
Guest
Despot

Ralf, true. Your code might test the boundaries (I haven’t checked it – it seems it does). Mine doesn’t.

If you go for checking leap years you might run into a problem. Also it might be an issue if you use this for production its much slower than using a combination of simple regex + some custom code. Read the below comment (from me) for more on the matter.

Cheers and happy coding ;)

Despot
Guest
Despot

mkyong, your approach is ok, but has some flaws in it. The leap year calculation is not correct. 1900 is not a leap year (http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm). Also you are using too many if else statements. A switch in there would be better.

I tested couple of approaches for speed and you can see the results here: http://stackoverflow.com/questions/2149680/regex-date-format-validation-on-java/18252071#18252071

quinqui
Guest
quinqui

Thank you! It works great! :D

spprem89@gmail.com
Guest
spprem89@gmail.com

Excellent Job Yong…. You Blog is Very Usefull for developers as well as students…

Joan
Guest
Joan

Awesome!
Thanks

ravi
Guest
ravi

I need to get the pattern of the given date String.
please help me .

munnaf
Guest
munnaf

thank you. this is superb.

Diogene K.
Guest
Diogene K.

Thank you so much for your very helpful article. I like the way regexes help in validating data. The expression above will help me to derive some other ones to validate dates. Blessings to you from the Almighty.

Sandy
Guest
Sandy

Shouldn’t “^ and $” be used.I tried mentioned code and changed it for MM/DD/YYYY.But it was accepting 13/14 as valid date.Then I used “^ and $” as told at http://stackoverflow.com/questions/2137929/how-can-i-use-a-regular-expression-to-validate-month-input .And now its working as expected!!!!!!!

Arun.R.U
Guest
Arun.R.U

Hi mkyong,

How are you ? is it possible to make regular expression global ? I wanted to use xml validation in struts 2, but i am using same reguler expressions in all the xml so i wanted to make it global, do you have any idea about this ?

Thanks….

Ryan
Guest
Ryan

I’m not really a Java dev, but I think I might have a slight improvement to John Smith’s answer. I do like his solution better than yours though simply because it’s easy to immediately understand what is going on. Your solution could be improved if you applied a Compose Method refactoring. :)

public boolean dateValid(String d) {
SimpleDateFormat sdf = new SimpleDateFormat(“M/d/yyyy”);
try {
sdf.setLenient(false);
Date dx = sdf.parse(d);
return true;
} catch (ParseException e) {
return false;
}
}

trackback
Java Regular Expression Tutorials | J2EE Web Development Tutorials

[…] Date regular expression Date regular expression example in Java and unit tested with TestNG. (0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\d\d) […]

trackback
10 Java Regular Expression Examples You Should Know | Regular Expressions

[…] ==> See the explanation and example here […]

PhiLho
Guest
PhiLho
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… Read more »
Franck Arnulfo
Guest
Franck Arnulfo

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 ).

John Smith
Guest
John Smith

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.

John Smith
Guest
John Smith

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.

John Smith
Guest
John Smith
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; } }
Despot
Guest
Despot

This approach will not take care of dates like 0002/001/0003.
Also this approach is slower (if that matters to you).