В одном из проектов, в котором мне посчастливилось участвовать, заказчик хотел вводить в поле дату не только с помощью календаря, написанного на JavaScript, но и вручную, что было чревато множественными ошибками. Поэтому, пришлось применять валидацию для данных, введенных в это поле.

Ниже я предлагаю рассмотреть регулярное выражение для валидации даты, однако, этот пример я привожу только для изучения регулярных выражений. Этот код слишком громоздкий и не очень удобен для чтения и отладки. Более применимый код я привожу после модульного теста.

Unit-тест написан с использованием библиотеки TestNG 5.10. Как запустить тесты TestNG в Eclipse читайте здесь.

Выражение:

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

Комментарий:

(			        # начало первой группы
0?[1-9]		    #  01-09 или 1-9
|             #  или
[12][0-9]		  #  10-19 или 20-29
|			        #  или
3[01]			    #  30, 31
) 			      # конец первой группы
/			        #  далее символ "/"
(			        # начало второй группы
0?[1-9]		    #	  01-09 или 1-9
|			        #	  или
1[012]		    #	  10,11,12
)			        # конец второй группы
/			        #	  далее символ "/"
(			        #	начало третьей группы
(19|20)\\d\\d	#	    19[0-9][0-9] или 20[0-9][0-9]
)		          #	конец третьей группы

Пример на Java:

package ru.topcode.skillbase.regexp;
 
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);
	}
 
	/**
	 * Валидация даты с помощью регулярного выражения
	 * 
	 * @param date
	 *            валидируемая дата
	 * @return true валидный формат, false не валидный формат
	 */
	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; // только в 1,3,5,7,8,10,12 месяцах 31 день
				} else if (month.equals("2") || month.equals("02")) {
					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;
		}
	}
}

Unit Test:

package ru.topcode.skillbase.regexp.test;
 
import org.testng.Assert;
import org.testng.annotations.*;
 
import ru.topcode.skillbase.regexp.DateValidator;
 
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 + " , " + valid);
		Assert.assertEquals(true, valid);
	}
 
	@Test(dataProvider = "InvalidDateProvider", dependsOnMethods = "ValidDateTest")
	public void InValidDateTest(String date) {
		boolean valid = dateValidator.validate(date);
		System.out.println("Валидность даты : " + date + " , " + valid);
		Assert.assertEquals(false, valid);
	}
}

Результат выполнения теста:

Валидность даты : 1/1/2010 , true
Валидность даты : 01/01/2020 , true
Валидность даты : 31/1/2010 , true
Валидность даты : 31/01/2020 , true
Валидность даты : 29/2/2008 , true
Валидность даты : 29/02/2008 , true
Валидность даты : 28/2/2009 , true
Валидность даты : 28/02/2009 , true
Валидность даты : 31/3/2010 , true
Валидность даты : 31/03/2010 , true
Валидность даты : 30/4/2010 , true
Валидность даты : 30/04/2010 , true
Валидность даты : 31/5/2010 , true
Валидность даты : 31/05/2010 , true
Валидность даты : 30/6/2010 , true
Валидность даты : 30/06/2010 , true
Валидность даты : 31/7/2010 , true
Валидность даты : 31/07/2010 , true
Валидность даты : 31/8/2010 , true
Валидность даты : 31/08/2010 , true
Валидность даты : 30/9/2010 , true
Валидность даты : 30/09/2010 , true
Валидность даты : 31/10/2010 , true
Валидность даты : 31/10/2010 , true
Валидность даты : 30/11/2010 , true
Валидность даты : 30/11/2010 , true
Валидность даты : 31/12/2010 , true
Валидность даты : 31/12/2010 , true
Валидность даты : 32/1/2010 , false
Валидность даты : 32/01/2020 , false
Валидность даты : 1/13/2010 , false
Валидность даты : 01/01/1820 , false
Валидность даты : 29/2/2007 , false
Валидность даты : 29/02/2007 , false
Валидность даты : 30/2/2008 , false
Валидность даты : 31/02/2008 , false
Валидность даты : 29/a/2008 , false
Валидность даты : a/02/2008 , false
Валидность даты : 333/2/2008 , false
Валидность даты : 29/02/200a , false
Валидность даты : 31/4/2010 , false
Валидность даты : 31/04/2010 , false
Валидность даты : 31/6/2010 , false
Валидность даты : 31/06/2010 , false
Валидность даты : 31/9/2010 , false
Валидность даты : 31/09/2010 , false
Валидность даты : 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")
 
===============================================
    ru.topcode.skillbase.regexp.test.DateValidatorTest
    Tests run: 47, Failures: 0, Skips: 0
===============================================

А теперь более удобоваримый код для валидации даты, но уже без регулярных выражений.

package ru.topcode.skillbase.regexp;
 
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class DateValidator {
	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;
		}
	}
}

Unit-тест для этого кода выглядит точно также, как и для первого примера, поэтому приводить его здесь не буду.

Для углубленного изучения регулярных выражений в Java рекомендую посетить соответствующий раздел The Java Tutorials и изучить книги из раздела RegExp в библиотеке TopCode.