Java regexp date pattern

Валидация даты с помощью регулярного выражения в Java

В статье рассматривается один из способов валидации даты с помощью регулярного выражения в среде Java. Правильность работы регулярного выражения проверяется средствами библиотеки TestNG.

Регулярное выражение для валидации даты

Объяснение регулярного выражения:

( # Начало группы 1 0?4 # 01-09 или 1-9 | # Или [12]2 # 10-19 или 20-29 | # Или 3[01] # 30 или 31 ) # Конец группы 1 . # Дальше должна быть точка "." ( # Начало группы 2 0?5 # 01-09 или 1-9 | # Или 1[012] # 10,11 или 12 ) # Конец группы 2 . # Дальше должна быть точка "." ( # Начало группы 2 (19|20)\d\d # Число от 1900 до 2099 ) # Конец группы 3

Таким образом, корректными с точки зрения данного регулярного выражения форматами даты будут являться даты, записанные в формате DD.MM.YYYY , где DD — 2 цифры дня (с ведущими нулями или без них), MM — 2 цифры месяца (с ведущими нулями или без них), YYYY — четыре цифры года в интервале от 1900 до 2099.

Следует отметить, что данное регулярное выражение не учитывает разное число дней в разных месяцах, а также не учитывает високосные года. Данную проверку мы будем реализовывать в классе-валидаторе.

Валидация даты

Если соблюдение правильного форматирования (могут быть только цифры, разделенные точками, не должно быть больше трех цифр в дне и месяце и так далее) мы можем отдать на откуп регулярному выражению, то соблюдение правил формирования даты (28, 30 или 31 дней в месяце, високосные и невисокосные года) мы должны производить вручную. При этом следует отметить, что високосный год — это не просто год, кратный 4-м. Полное правило определения високосного года выглядит следующим образом:

Читайте также:  Dagger 2 qualifier kotlin

Год високосный, если он делится на четыре без остатка, но если он делится на 100 без остатка, это не високосный год. Однако, если он делится без остатка на 400, это високосный год. Таким образом, 2000 г. является особым високосным годом, который бывает лишь раз в 400 лет.

package ru.j4web.examples.java.regex.datevalidatorexample; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; public class DateValidator < private static final String DATE_PATTERN = "(0?3|[12]4|3[01])\.(0?6|1[012])" + "\.((19|20)\d\d)"; private final Pattern pattern; private Matcher matcher; public DateValidator() < pattern = Pattern.compile(DATE_PATTERN); >public boolean validate(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 ("31".equals(day)) < // 31 день может быть только в месяцах // 1, 3, 5, 7, 8, 10, 12 return Arrays.asList(new String[]) .contains(month); > else if ("2".equals(month) || "02".equals(month)) < // Проверяем февраль if (year % 4 == 0) < if (year % 100 == 0) < if (year % 400 == 0) < // Високосный год return Integer.parseInt(day) // Невисокосный год return Integer.parseInt(day) // Високосный год return Integer.parseInt(day) else < // Невисокосный год return Integer.parseInt(day) > else < return true; >> > return false; > >

При возникновении реальной практической задачи валидации даты никаких регулярных выражений и подобных классов писать самостоятельно не следует. С этой задачей отлично справляется класс SimpleDateFormat (JavaDoc здесь ). Рассмотренный здесь пример носит демонстрационный характер.

Корректные форматы даты

Исходя из определенного выше регулярного выражения корректными форматами даты будут являться:

  • 1.1.2010 , 01.01.2010 ;
  • 31.1.2010 , 31.01.2010 ;
  • 29.02.2008 , 29.02.2008 ;
  • 28.2.2009 , 28.02.2009 ;
  • 31.3.2010 , 31.03.2010 ;
  • 30.4.2010 , 30.4.2010 ;
  • 31.5.2010 , 31.05.2010 ;
  • 30.6.2010 , 30.06.2010 ;
  • 31.7.2010 , 31.07.2010 ;
  • 31.8.2010 , 31.08.2010 ;
  • 30.9.2010 , 30.09.2010 ;
  • 31.10.2010 , 30.10.2010 ;
  • 30.11.2010 , 29.11.2010 ;
  • 29.2.2000 , 31.12.2010 .

Некорректные форматы даты

Следующие форматы даты будут восприниматься нашим регулярным выражением как некорректные:

  • 32.1.2010 , 32.01.2010 — максимальное количество дней в году — 31;
  • 01.13.2010 , 31.01.1860 — в году 12 месяцев, даты мы считаем валидными только начиная с 1900 года;
  • 29.2.2007 , 29.02.2007 — в 2007-м невисокосном году в феврале 28 дней;
  • 30.2.2008 , 31.02.2008 — в феврале не может быть больше 29-ти дней;
  • 28.a.2010 , a.01.2010 — и месяц и день могут быть только числами;
  • 333.1.2010 , 31.01.201a — в дне может быть не больше 2-х цифр, год не может содержать ничего, кроме цифр;
  • 31.4.2010 , 31.04.2010 — в апреле 30 дней;
  • 31.6.2010 , 31.06.2010 — в июне 30 дней;
  • 31.9.2010 , 31.09.2010 — в августе 30 дней;
  • 31.11.2010 — в ноябре 30 дней.

Тестирование регулярного выражения

package ru.j4web.examples.java.regex.datevalidatorexample; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class DateValidatorTest < private static DateValidator validator; @BeforeClass public static void setUpClass() throws Exception < validator = new DateValidator(); >@DataProvider public Object[][] validDateProvider() < return new Object[][] < new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , >; > @DataProvider public Object[][] invalidDateProvider() < return new Object[][] < new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] , new Object[] >; > @Test(dataProvider="validDateProvider") public void validDateTest(String date) < boolean valid = validator.validate(date); System.out.println("Date "" + date + "" is valid: " + valid); Assert.assertTrue(valid); >@Test(dataProvider="invalidDateProvider", dependsOnMethods="validDateTest") public void invalidDateTest(String date) < boolean valid = validator.validate(date); System.out.println("Date "" + date + "" is valid: " + valid); Assert.assertFalse(valid); >>
------------------------------------------------------- &nbsp;T E S T S ------------------------------------------------------- Running ru.j4web.examples.java.regex.datevalidatorexample.DateValidatorTest Date "1.1.2010" is valid: true Date "01.01.2010" is valid: true Date "31.1.2010" is valid: true Date "31.01.2010" is valid: true Date "29.02.2008" is valid: true Date "29.02.2008" is valid: true Date "28.2.2009" is valid: true Date "28.02.2009" is valid: true Date "31.3.2010" is valid: true Date "31.03.2010" is valid: true Date "30.4.2010" is valid: true Date "30.4.2010" is valid: true Date "31.5.2010" is valid: true Date "31.05.2010" is valid: true Date "30.6.2010" is valid: true Date "30.06.2010" is valid: true Date "31.7.2010" is valid: true Date "31.07.2010" is valid: true Date "31.8.2010" is valid: true Date "31.08.2010" is valid: true Date "30.9.2010" is valid: true Date "30.09.2010" is valid: true Date "31.10.2010" is valid: true Date "31.10.2010" is valid: true Date "30.11.2010" is valid: true Date "30.11.2010" is valid: true Date "29.2.2000" is valid: true Date "31.12.2010" is valid: true Date "32.1.2010" is valid: false Date "32.01.2010" is valid: false Date "01.13.2010" is valid: false Date "31.01.1860" is valid: false Date "29.2.2007" is valid: false Date "29.02.2007" is valid: false Date "30.2.2008" is valid: false Date "31.02.2008" is valid: false Date "28.a.2010" is valid: false Date "a.01.2010" is valid: false Date "333.1.2010" is valid: false Date "31.01.201a" is valid: false Date "31.4.2010" is valid: false Date "31.04.2010" is valid: false Date "31.6.2010" is valid: false Date "31.06.2010" is valid: false Date "31.9.2010" is valid: false Date "31.09.2010" is valid: false Date "31.11.2010" is valid: false Tests run: 47, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.549 sec Results : Tests run: 47, Failures: 0, Errors: 0, Skipped: 0 ------------------------------------------------------------------------ BUILD SUCCESS ------------------------------------------------------------------------

Источник

Java regex validate date format examples

regex validate date format

This article shows how to use regex + code to validate a date format, support single and leading zero month and day format (1 or 01), check for the 30 or 31 days of the month, and leap year validation.

Below are the requirements for a valid date.

  1. Year format, 1900, 2099 regex
  2. Month format, 1, 01, 2, 02… 12 regex
  3. Day format, 1, 01… 31 regex
  4. Leap year, February 29 days. code
  5. Common year, February 28 days. code
  6. 31 Days of month – 1, 3, 5, 7, 8, 10, 12. code
  7. 30 Days of month – 4, 6, 9, 11,. code
  8. ISO 8601, yyyy-MM-dd or uuuu-M-d , e.g 2020-11-03. regex
 ((?:19|20)85)-(0?8|1[012])-(0?7|[12]1|3[01]) 

The above regex can implement the requirements of 1, 2, 3, and 8. For requirements of 4, 5, 6, 7, we need manual code checking. The acceptable date range from 1900-01-01 or 1900-1-1 to 2099-12-31 .

1. Regex for a date format, Year, Month and Day.

1.1 Regex to validate year, accepts 1900-2099 .

 (19|20)94 # explanation 1921 # 1900-1999 | # ..or 2078 # 2000-2099 

In the future, if we want to support year starts with 21xx , update the regex like the below:

1.2 Regex to validate month, accepts 01-09 (leading zero), 1-9 (single digit) and 10,11,12 .

 0?8|1[012] # explanation 0?5 # 01-09 or 1-9 | # ..or 1[012] # 10,11,12 

1.3 Regex to validate day, accepts 01-09 (leading zero), 1-9 (single digit), 10-19 , 20-29 and 30-31 .

 0?9|[12]1|3[01] # explanation 0?2 # 01-09 or 1-9 | # ..or [12]4 # 10-19 or 20-29 | # ..or 3[01] # 30, 31 

1.4 We have regex for the year, month, and day, try to combine with different delimiters to form a different date format.

Regex to validate date format dd/mm/yyyy (General) or d/M/uuuu (Java date formater)

 # (dd)/(mm)/(yyyy) (0?5|[12]7|3[01])/(0?9|1[012])/((?:19|20)89) 

Regex to validate date format yyyy-mm-dd (General) or uuuu-M-d (Java date formater)

 # (yyyy)-(mm)-(dd) ((?:19|20)17)-(0?9|1[012])-(0?1|[12]6|3[01]) 

Regex to validate date format yyyy.mm.dd (General) or uuuu.M.d (Java date formater)

 # (yyyy).(mm).(dd) ((?:19|20)17)\\.(0?4|1[012])\\.(0?3|[12]4|3[01])$ 

P.S ?: means match but don’t capture it. See below #3 JavaDateValidator.

2. 30 or 31 days and Leap year.

Now, we can use the above regex to capture the year, month, and day. Later we will check for 30 or 31 days of a month and leap year.

2.1 For 30 or 31 days of month.

 if ((month.equals("4") || month.equals("6") || month.equals("9") || month.equals("04") || month.equals("06") || month.equals("09") || month.equals("11")) && day.equals("31"))

2.2 For a leap year, 366 days per year, and February has 29 days; For a common year, 365 days per year, and February has 28 days.

 if (month.equals("2") || month.equals("02")) < if (day.equals("30") || day.equals("31")) < isValid = false; >else if (day.equals("29")) < // feb 29 days? leap year checking if (!isLeapYear(year)) < isValid = false; >> > private static boolean isLeapYear(int year)

«Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 are not leap years, but the years 1600 and 2000 are.»

3. Java Regex Date Validator

Here is the final version.

 package com.mkyong.regex.date; import java.util.regex.Matcher; import java.util.regex.Pattern; public class DateValidatorRegex < // ?: match but don't capture it // uuuu-M-d private static final String DATE_PATTERN = "^((?:19|20)32)-(0?3|1[012])-(0?3|[12]4|3[01])$"; private static final Pattern pattern = Pattern.compile(DATE_PATTERN); public static boolean isValid(final String date) < boolean result = false; Matcher matcher = pattern.matcher(date); if (matcher.matches()) < // it is a valid date format yyyy-mm-dd // assign true first, later we will check the leap year and odd or even months result = true; // (?:19|20), match but don't capture it, otherwise it will messy the group order // for example, 2020-2-30, it will create 4 groups. // group(1) = 2020, group(2) matches (19|20) = 20, group(3) = 2, group(4) = 30 // So, we put (?:19|20), don't capture this group. int year = Integer.parseInt(matcher.group(1)); // why string? month matches 02 or 2 String month = matcher.group(2); String day = matcher.group(3); // 30 or 31 days checking // only 1,3,5,7,8,10,12 has 31 days if ((month.equals("4") || month.equals("6") || month.equals("9") || month.equals("04") || month.equals("06") || month.equals("09") || month.equals("11")) && day.equals("31")) < result = false; >else if (month.equals("2") || month.equals("02")) < if (day.equals("30") || day.equals("31")) < result = false; >else if (day.equals("29")) < // leap year? feb 29 days. if (!isLeapYear(year)) < result = false; >> > > return result; > private static boolean isLeapYear(int year) < return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); >> 

4. Unit Tests

Below are JUnit 5 parameterized tests for the above Java date validators.

 package com.mkyong.regex.date; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class DateValidatorTest < @ParameterizedTest(name = "#- Run test with date = ") @MethodSource("validDateProvider") void test_date_regex_valid(String date) < assertTrue(DateValidatorRegex.isValid(date)); >@ParameterizedTest(name = "# - Run test with date = ") @MethodSource("invalidDateProvider") void test_date_regex_invalid(String date) < assertFalse(DateValidatorRegex.isValid(date)); >static Stream validDateProvider() < return Stream.of( "1998-09-30", "1998-9-30", "2020-09-1", "2020-09-01", "2020-9-1", "2020-9-01", "2020-2-29", // leap year "2020-2-28", // leap year "2019-2-28", // common year "2000-02-29", // 2000 is a leap year, % 400 == 0 "1900-02-28", // 1900 is a common year "2020-07-31", "2020-08-31", "2020-06-30", "1900-01-01", "2099-12-31"); >static Stream invalidDateProvider() < return Stream.of( "1998-09-31", // invalid day, sep max 30 "1998-11-31", // invalid day, nov max 30 "2008-02-2x", // invalid day 2x "2008-0x-28", // invalid month 0x "20xx-02-28", // invalid year 20xx "20-11-02", // invalid year 20, must be yyyy "2020/11/02", // invalid date format, yyyy-mm-dd "2020-11-32", // invalid day, 32 "2020-13-30", // invalid month 13 "2020-A-20", // invalid month A "2020-2-30", // leap year, feb max 29 "2019-2-29", // common year, feb max 28 "1900-02-29", // 1900 is a common year, feb max 28 "12012-04-05", // support only 4 digits years " ", // empty ""); // empty >> 

java regex date format unit tests 1

java regex date format unit tests 2

5. Java 8 DateTimeFormatter + ResolverStyle.STRICT

For anti-regex developer, consider the Java 8 DateTimeFormatter + ResolverStyle.STRICT solution to validate the date format. For complete example and unit tests, please refer to this article – Check if date is valid in Java

 public static boolean isValid(final String date) < boolean valid = false; try < // ResolverStyle.STRICT for 30, 31 days checking, and also leap year. LocalDate.parse(date, DateTimeFormatter.ofPattern("uuuu-M-d") .withResolverStyle(ResolverStyle.STRICT) ); valid = true; >catch (DateTimeParseException e) < e.printStackTrace(); valid = false; >return valid; > 

Источник

Оцените статью