В одном из проектов, в котором мне посчастливилось участвовать, заказчик хотел вводить в поле дату не только с помощью календаря, написанного на 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.


