2010-01-27 24 views
39

Me pregunto si hay una forma (tal vez con expresiones regulares) de validar que una entrada en una aplicación de escritorio Java es exactamente una cadena formateada como: "AAAA-MM-DD".Validación de formato de fecha Regex en Java

He buscado pero sin éxito.

Gracias

+6

¿Desea permitir cualquier año, y también números inválidos durante meses y días? 9999-99-99 por ejemplo? ¿Qué pasa con las fechas no válidas como 2009-02-29? –

+0

Pregunta relacionada, solo Regex: http: // stackoverflow.com/q/8647893/8384 – McKay

Respuesta

62

Usar la siguiente expresión regular:

^\d{4}-\d{2}-\d{2}$ 

como en

if (str.matches("\\d{4}-\\d{2}-\\d{2}")) { 
    ... 
} 

Con el método matches, los anclajes ^ y $ (principio y final de la cadena, respectivamente) están presentes implícitamente.

+3

Pero esto valida solo el formato de fecha, no la fecha real es válida o no. Me gusta más la idea de @Steve B o, si lo recuerdo bien, también debe haber una buena validación disponible en el paquete de validación de bienes comunes. –

+2

Las fechas son difíciles. Aunque puede verificar el FORMATO de la entrada, no puede verificar el CONTENIDO con una expresión regular simple. Recomendamos que use un analizador creado para la tarea. –

+0

@ChrisNava recomendado, sí. Es posible hacer la validación de fecha en expresiones regulares. Pero no recomendado. http://stackoverflow.com/q/8647893/8384 – McKay

29

Necesita más de regex, por ejemplo "9999-99-00" no es una fecha válida. Hay una clase SimpleDateFormat que está diseñada para hacer esto. Más pesado, pero más completo.

p. Ej.

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 

boolean isValidDate(string input) { 
    try { 
      format.parse(input); 
      return true; 
    } 
    catch(ParseException e){ 
      return false; 
    } 
} 

Desafortunadamente, SimpleDateFormat es tanto de peso pesado y no apta para subprocesos.

+15

no se olvide de llamar a 'setLenient (false)' si desea que SimpleDateFormat rechace las fechas no válidas. Su código aceptará "9999-99-00" (sería 'Wed Feb 28 00:00:00 CET 10007') –

+1

Gracias Carlos, ni siquiera sabía que reconocería entradas como 2009-20-20. Gracias :) – Sheldon

+0

y otro punto: SimpleDateFormat no comprueba el formato: "2010-1-8", "10-001-002", ... se aceptarán –

2

Construir un SimpleDateFormat con la máscara, y luego llamar a: SimpleDateFormat.parse (String s, ParsePosition p)

+0

Puede llamar simplemente al método de 1 argumento 'parse (String)' en lugar de uno de dos argumentos. _ _ _ Y no olvides llamar 'setLenient (false)' si quieres que SimpleDateFormat rechace las fechas no válidas. –

1

Para un control preciso, considere un InputVerifier utilizando el SimpleDateFormat("YYYY-MM-dd") sugerido por Steve B.

22

Poniendo todo junto:

  • REGEX no valida los valores (como "2010-19-19")
  • SimpleDateFormat no comprueba el formato ("02/01/2010", "1-0002-003" son aceptados)

es necesario utilizar tanto para validar el formato y el valor:

public static boolean isValid(String text) { 
    if (text == null || !text.matches("\\d{4}-[01]\\d-[0-3]\\d")) 
     return false; 
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 
    df.setLenient(false); 
    try { 
     df.parse(text); 
     return true; 
    } catch (ParseException ex) { 
     return false; 
    } 
} 



Se puede utilizar un ThreadLocal para evitar la creación de un nuevo SimpleDateFormat para cada llamada.
Se necesita en un contexto multi-hilo ya que el SimpleDateFormat no es hilo de seguridad:

private static final ThreadLocal<SimpleDateFormat> format = new ThreadLocal<SimpleDateFormat>() { 
    @Override 
    protected SimpleDateFormat initialValue() { 
     SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 
     df.setLenient(false); 
     System.out.println("created"); 
     return df; 
    } 
}; 

public static boolean isValid(String text) { 
    if (text == null || !text.matches("\\d{4}-[01]\\d-[0-3]\\d")) 
     return false; 
    try { 
     format.get().parse(text); 
     return true; 
    } catch (ParseException ex) { 
     return false; 
    } 
} 

(mismo se puede hacer para un Matcher, que tampoco es seguro para subprocesos)

+0

regex puede validar los valores – McKay

11

Esto lo hará de expresiones regulares: "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$" Esto se ocupará de los formatos válidos y las fechas válidas. No validará los días correctos del mes, es decir, año bisiesto.

String regex = "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$"; 

Assert.assertTrue("Date: matched.", Pattern.matches(regex, "2011-1-1")); 
Assert.assertFalse("Date (month): not matched.", Pattern.matches(regex, "2011-13-1")); 

¡Buena suerte!

7

me gustaría ir con una simple expresión regular que comprobará que los días no tiene más de 31 días y meses no más de 12. Algo como:

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

Este es el formato "dd-MM yyyy ". Puede ajustarlo según sus necesidades (por ejemplo, quitar el? Para hacer el 0 inicial necesario - ahora es opcional), y luego usar una lógica personalizada para reducir las reglas específicas como años bisiempos, número de días de febrero, caso y otros meses número de días casos. Vea el código de DateChecker a continuación.

Estoy eligiendo este enfoque ya que probé que este es el mejor cuando se tiene en cuenta el rendimiento. Comprobé este (1er) enfoque versus el 2º enfoque de validación de una fecha frente a una expresión regular que se ocupa de los demás casos de uso, y el 3er enfoque de usar la misma expresión regular anterior en combinación con SimpleDateFormat.parse (fecha).
El primer enfoque fue 4 veces más rápido que el segundo enfoque, y 8 veces más rápido que el 3er enfoque. Consulte el verificador de fecha independiente y la clase principal del comprobador de rendimiento en la parte inferior. Una cosa que dejo sin marcar es el (los) enfoque (s) del tiempo joda. (La biblioteca de fecha/hora más eficiente).

Fecha código corrector:

class DateChecker { 

    private Matcher matcher; 
    private Pattern pattern; 

    public DateChecker(String regex) { 
     pattern = Pattern.compile(regex); 
    } 

    /** 
    * Checks if the date format is a valid. 
    * Uses the regex pattern to match the date first. 
    * Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered). 
    * 
    * @param date the date that needs to be checked. 
    * @return if the date is of an valid format or not. 
    */ 
    public boolean check(final String date) { 
     matcher = pattern.matcher(date); 
     if (matcher.matches()) { 
      matcher.reset(); 
      if (matcher.find()) { 
       int day = Integer.parseInt(matcher.group(1)); 
       int month = Integer.parseInt(matcher.group(2)); 
       int year = Integer.parseInt(matcher.group(3)); 

       switch (month) { 
       case 1: 
       case 3: 
       case 5: 
       case 7: 
       case 8: 
       case 10: 
       case 12: return day < 32; 
       case 4: 
       case 6: 
       case 9: 
       case 11: return day < 31; 
       case 2: 
        int modulo100 = year % 100; 
        //http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm 
        if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) { 
         //its a leap year 
         return day < 30; 
        } else { 
         return day < 29; 
        } 
       default: 
        break; 
       } 
      } 
     } 
     return false; 
    } 

    public String getRegex() { 
     return pattern.pattern(); 
    } 
} 

Fecha de cheques/pruebas y pruebas de rendimiento:

import java.text.ParseException; 
import java.text.SimpleDateFormat; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

public class Tester { 

    private static final String[] validDateStrings = new String[]{ 
     "1-1-2000", //leading 0s for day and month optional 
     "01-1-2000", //leading 0 for month only optional 
     "1-01-2000", //leading 0 for day only optional 
     "01-01-1800", //first accepted date 
     "31-12-2199", //last accepted date 
     "31-01-2000", //January has 31 days 
     "31-03-2000", //March has 31 days 
     "31-05-2000", //May has 31 days 
     "31-07-2000", //July has 31 days 
     "31-08-2000", //August has 31 days 
     "31-10-2000", //October has 31 days 
     "31-12-2000", //December has 31 days 
     "30-04-2000", //April has 30 days 
     "30-06-2000", //June has 30 days 
     "30-09-2000", //September has 30 days 
     "30-11-2000", //November has 30 days 
    }; 
    private static final String[] invalidDateStrings = new String[]{ 
     "00-01-2000", //there is no 0-th day 
     "01-00-2000", //there is no 0-th month 
     "31-12-1799", //out of lower boundary date 
     "01-01-2200", //out of high boundary date 
     "32-01-2000", //January doesn't have 32 days 
     "32-03-2000", //March doesn't have 32 days 
     "32-05-2000", //May doesn't have 32 days 
     "32-07-2000", //July doesn't have 32 days 
     "32-08-2000", //August doesn't have 32 days 
     "32-10-2000", //October doesn't have 32 days 
     "32-12-2000", //December doesn't have 32 days 
     "31-04-2000", //April doesn't have 31 days 
     "31-06-2000", //June doesn't have 31 days 
     "31-09-2000", //September doesn't have 31 days 
     "31-11-2000", //November doesn't have 31 days 
     "001-02-2000", //SimpleDateFormat valid date (day with leading 0s) even with lenient set to false 
     "1-0002-2000", //SimpleDateFormat valid date (month with leading 0s) even with lenient set to false 
     "01-02-0003", //SimpleDateFormat valid date (year with leading 0s) even with lenient set to false 
     "01.01-2000", //. invalid separator between day and month 
     "01-01.2000", //. invalid separator between month and year 
     "01/01-2000", /// invalid separator between day and month 
     "01-01/2000", /// invalid separator between month and year 
     "01_01-2000", //_ invalid separator between day and month 
     "01-01_2000", //_ invalid separator between month and year 
     "01-01-2000-12345", //only whole string should be matched 
     "01-13-2000", //month bigger than 13 
    }; 

    /** 
    * These constants will be used to generate the valid and invalid boundary dates for the leap years. (For no leap year, Feb. 28 valid and Feb. 29 invalid; for a leap year Feb. 29 valid and Feb. 30 invalid) 
    */ 
    private static final int LEAP_STEP = 4; 
    private static final int YEAR_START = 1800; 
    private static final int YEAR_END = 2199; 

    /** 
    * This date regex will find matches for valid dates between 1800 and 2199 in the format of "dd-MM-yyyy". 
    * The leading 0 is optional. 
    */ 
    private static final String DATE_REGEX = "((0?[1-9]|[12][0-9]|3[01])-(0?[13578]|1[02])-(18|19|20|21)[0-9]{2})|((0?[1-9]|[12][0-9]|30)-(0?[469]|11)-(18|19|20|21)[0-9]{2})|((0?[1-9]|1[0-9]|2[0-8])-(0?2)-(18|19|20|21)[0-9]{2})|(29-(0?2)-(((18|19|20|21)(04|08|[2468][048]|[13579][26]))|2000))"; 

    /** 
    * This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match. 
    * Keep in mind that String.matches tries to match only the whole string. 
    */ 
    private static final String DATE_REGEX_ONLY_WHOLE_STRING = "^" + DATE_REGEX + "$"; 

    /** 
    * The simple regex (without checking for 31 day months and leap years): 
    */ 
    private static final String DATE_REGEX_SIMPLE = "(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((18|19|20|21)\\d\\d)"; 

    /** 
    * This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match. 
    */ 
    private static final String DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING = "^" + DATE_REGEX_SIMPLE + "$"; 

    private static final SimpleDateFormat SDF = new SimpleDateFormat("dd-MM-yyyy"); 
    static { 
     SDF.setLenient(false); 
    } 

    private static final DateChecker dateValidatorSimple = new DateChecker(DATE_REGEX_SIMPLE); 
    private static final DateChecker dateValidatorSimpleOnlyWholeString = new DateChecker(DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING); 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     DateTimeStatistics dateTimeStatistics = new DateTimeStatistics(); 
     boolean shouldMatch = true; 
     for (int i = 0; i < validDateStrings.length; i++) { 
      String validDate = validDateStrings[i]; 
      matchAssertAndPopulateTimes(
        dateTimeStatistics, 
        shouldMatch, validDate); 
     } 

     shouldMatch = false; 
     for (int i = 0; i < invalidDateStrings.length; i++) { 
      String invalidDate = invalidDateStrings[i]; 

      matchAssertAndPopulateTimes(dateTimeStatistics, 
        shouldMatch, invalidDate); 
     } 

     for (int year = YEAR_START; year < (YEAR_END + 1); year++) { 
      FebruaryBoundaryDates februaryBoundaryDates = createValidAndInvalidFebruaryBoundaryDateStringsFromYear(year); 
      shouldMatch = true; 
      matchAssertAndPopulateTimes(dateTimeStatistics, 
        shouldMatch, februaryBoundaryDates.getValidFebruaryBoundaryDateString()); 
      shouldMatch = false; 
      matchAssertAndPopulateTimes(dateTimeStatistics, 
        shouldMatch, februaryBoundaryDates.getInvalidFebruaryBoundaryDateString()); 
     } 

     dateTimeStatistics.calculateAvarageTimesAndPrint(); 
    } 

    private static void matchAssertAndPopulateTimes(
      DateTimeStatistics dateTimeStatistics, 
      boolean shouldMatch, String date) { 
     dateTimeStatistics.addDate(date); 
     matchAndPopulateTimeToMatch(date, DATE_REGEX, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegex()); 
     matchAndPopulateTimeToMatch(date, DATE_REGEX_ONLY_WHOLE_STRING, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegexOnlyWholeString()); 
     boolean matchesSimpleDateFormat = matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(date, dateTimeStatistics.getTimesTakenWithSimpleDateFormatParse()); 
     matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
       dateTimeStatistics.getTimesTakenWithDateRegexSimple(), shouldMatch, 
       date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE); 
     matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
       dateTimeStatistics.getTimesTakenWithDateRegexSimpleOnlyWholeString(), shouldMatch, 
       date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING); 

     matchAndPopulateTimeToMatch(date, dateValidatorSimple, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimple()); 
     matchAndPopulateTimeToMatch(date, dateValidatorSimpleOnlyWholeString, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimpleOnlyWholeString()); 
    } 

    private static void matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
      List<Long> times, 
      boolean shouldMatch, String date, boolean matchesSimpleDateFormat, String regex) { 
     boolean matchesFromRegex = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times); 
     assert !((matchesSimpleDateFormat && matchesFromRegex)^shouldMatch) : "Parsing with SimpleDateFormat and date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch; 
    } 

    private static void matchAndPopulateTimeToMatch(String date, String regex, boolean shouldMatch, List<Long> times) { 
     boolean matches = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times); 
     assert !(matches^shouldMatch) : "date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch; 
    } 

    private static void matchAndPopulateTimeToMatch(String date, DateChecker dateValidator, boolean shouldMatch, List<Long> times) { 
     long timestampStart; 
     long timestampEnd; 
     boolean matches; 
     timestampStart = System.nanoTime(); 
     matches = dateValidator.check(date); 
     timestampEnd = System.nanoTime(); 
     times.add(timestampEnd - timestampStart); 
     assert !(matches^shouldMatch) : "date:" + date + "\ndateValidator with regex:" + dateValidator.getRegex() + "\nshouldMatch:" + shouldMatch; 
    } 

    private static boolean matchAndPopulateTimeToMatchAndReturnMatches(String date, String regex, List<Long> times) { 
     long timestampStart; 
     long timestampEnd; 
     boolean matches; 
     timestampStart = System.nanoTime(); 
     matches = date.matches(regex); 
     timestampEnd = System.nanoTime(); 
     times.add(timestampEnd - timestampStart); 
     return matches; 
    } 

    private static boolean matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(String date, List<Long> times) { 
     long timestampStart; 
     long timestampEnd; 
     boolean matches = true; 
     timestampStart = System.nanoTime(); 
     try { 
      SDF.parse(date); 
     } catch (ParseException e) { 
      matches = false; 
     } finally { 
      timestampEnd = System.nanoTime(); 
      times.add(timestampEnd - timestampStart); 
     } 
     return matches; 
    } 

    private static FebruaryBoundaryDates createValidAndInvalidFebruaryBoundaryDateStringsFromYear(int year) { 
     FebruaryBoundaryDates februaryBoundaryDates; 
     int modulo100 = year % 100; 
     //http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm 
     if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) { 
      februaryBoundaryDates = new FebruaryBoundaryDates(
        createFebruaryDateFromDayAndYear(29, year), 
        createFebruaryDateFromDayAndYear(30, year) 
        ); 
     } else { 
      februaryBoundaryDates = new FebruaryBoundaryDates(
        createFebruaryDateFromDayAndYear(28, year), 
        createFebruaryDateFromDayAndYear(29, year) 
        ); 
     } 
     return februaryBoundaryDates; 
    } 

    private static String createFebruaryDateFromDayAndYear(int day, int year) { 
     return String.format("%d-02-%d", day, year); 
    } 

    static class FebruaryBoundaryDates { 
     private String validFebruaryBoundaryDateString; 
     String invalidFebruaryBoundaryDateString; 
     public FebruaryBoundaryDates(String validFebruaryBoundaryDateString, 
       String invalidFebruaryBoundaryDateString) { 
      super(); 
      this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString; 
      this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString; 
     } 
     public String getValidFebruaryBoundaryDateString() { 
      return validFebruaryBoundaryDateString; 
     } 
     public void setValidFebruaryBoundaryDateString(
       String validFebruaryBoundaryDateString) { 
      this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString; 
     } 
     public String getInvalidFebruaryBoundaryDateString() { 
      return invalidFebruaryBoundaryDateString; 
     } 
     public void setInvalidFebruaryBoundaryDateString(
       String invalidFebruaryBoundaryDateString) { 
      this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString; 
     } 
    } 

    static class DateTimeStatistics { 
     private List<String> dates = new ArrayList<String>(); 
     private List<Long> timesTakenWithDateRegex = new ArrayList<Long>(); 
     private List<Long> timesTakenWithDateRegexOnlyWholeString = new ArrayList<Long>(); 
     private List<Long> timesTakenWithDateRegexSimple = new ArrayList<Long>(); 
     private List<Long> timesTakenWithDateRegexSimpleOnlyWholeString = new ArrayList<Long>(); 
     private List<Long> timesTakenWithSimpleDateFormatParse = new ArrayList<Long>(); 
     private List<Long> timesTakenWithdateValidatorSimple = new ArrayList<Long>(); 
     private List<Long> timesTakenWithdateValidatorSimpleOnlyWholeString = new ArrayList<Long>(); 
     public List<String> getDates() { 
      return dates; 
     } 
     public List<Long> getTimesTakenWithDateRegex() { 
      return timesTakenWithDateRegex; 
     } 
     public List<Long> getTimesTakenWithDateRegexOnlyWholeString() { 
      return timesTakenWithDateRegexOnlyWholeString; 
     } 
     public List<Long> getTimesTakenWithDateRegexSimple() { 
      return timesTakenWithDateRegexSimple; 
     } 
     public List<Long> getTimesTakenWithDateRegexSimpleOnlyWholeString() { 
      return timesTakenWithDateRegexSimpleOnlyWholeString; 
     } 
     public List<Long> getTimesTakenWithSimpleDateFormatParse() { 
      return timesTakenWithSimpleDateFormatParse; 
     } 
     public List<Long> getTimesTakenWithdateValidatorSimple() { 
      return timesTakenWithdateValidatorSimple; 
     } 
     public List<Long> getTimesTakenWithdateValidatorSimpleOnlyWholeString() { 
      return timesTakenWithdateValidatorSimpleOnlyWholeString; 
     } 
     public void addDate(String date) { 
      dates.add(date); 
     } 
     public void addTimesTakenWithDateRegex(long time) { 
      timesTakenWithDateRegex.add(time); 
     } 
     public void addTimesTakenWithDateRegexOnlyWholeString(long time) { 
      timesTakenWithDateRegexOnlyWholeString.add(time); 
     } 
     public void addTimesTakenWithDateRegexSimple(long time) { 
      timesTakenWithDateRegexSimple.add(time); 
     } 
     public void addTimesTakenWithDateRegexSimpleOnlyWholeString(long time) { 
      timesTakenWithDateRegexSimpleOnlyWholeString.add(time); 
     } 
     public void addTimesTakenWithSimpleDateFormatParse(long time) { 
      timesTakenWithSimpleDateFormatParse.add(time); 
     } 
     public void addTimesTakenWithdateValidatorSimple(long time) { 
      timesTakenWithdateValidatorSimple.add(time); 
     } 
     public void addTimesTakenWithdateValidatorSimpleOnlyWholeString(long time) { 
      timesTakenWithdateValidatorSimpleOnlyWholeString.add(time); 
     } 

     private void calculateAvarageTimesAndPrint() { 
      long[] sumOfTimes = new long[7]; 
      int timesSize = timesTakenWithDateRegex.size(); 
      for (int i = 0; i < timesSize; i++) { 
       sumOfTimes[0] += timesTakenWithDateRegex.get(i); 
       sumOfTimes[1] += timesTakenWithDateRegexOnlyWholeString.get(i); 
       sumOfTimes[2] += timesTakenWithDateRegexSimple.get(i); 
       sumOfTimes[3] += timesTakenWithDateRegexSimpleOnlyWholeString.get(i); 
       sumOfTimes[4] += timesTakenWithSimpleDateFormatParse.get(i); 
       sumOfTimes[5] += timesTakenWithdateValidatorSimple.get(i); 
       sumOfTimes[6] += timesTakenWithdateValidatorSimpleOnlyWholeString.get(i); 
      } 
      System.out.println("AVG from timesTakenWithDateRegex (in nanoseconds):" + (double) sumOfTimes[0]/timesSize); 
      System.out.println("AVG from timesTakenWithDateRegexOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[1]/timesSize); 
      System.out.println("AVG from timesTakenWithDateRegexSimple (in nanoseconds):" + (double) sumOfTimes[2]/timesSize); 
      System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[3]/timesSize); 
      System.out.println("AVG from timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) sumOfTimes[4]/timesSize); 
      System.out.println("AVG from timesTakenWithDateRegexSimple + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[2] + sumOfTimes[4])/timesSize); 
      System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[3] + sumOfTimes[4])/timesSize); 
      System.out.println("AVG from timesTakenWithdateValidatorSimple (in nanoseconds):" + (double) sumOfTimes[5]/timesSize); 
      System.out.println("AVG from timesTakenWithdateValidatorSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[6]/timesSize); 
     } 
    } 

    static class DateChecker { 

     private Matcher matcher; 
     private Pattern pattern; 

     public DateChecker(String regex) { 
      pattern = Pattern.compile(regex); 
     } 

     /** 
     * Checks if the date format is a valid. 
     * Uses the regex pattern to match the date first. 
     * Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered). 
     * 
     * @param date the date that needs to be checked. 
     * @return if the date is of an valid format or not. 
     */ 
     public boolean check(final String date) { 
      matcher = pattern.matcher(date); 
      if (matcher.matches()) { 
       matcher.reset(); 
       if (matcher.find()) { 
        int day = Integer.parseInt(matcher.group(1)); 
        int month = Integer.parseInt(matcher.group(2)); 
        int year = Integer.parseInt(matcher.group(3)); 

        switch (month) { 
        case 1: 
        case 3: 
        case 5: 
        case 7: 
        case 8: 
        case 10: 
        case 12: return day < 32; 
        case 4: 
        case 6: 
        case 9: 
        case 11: return day < 31; 
        case 2: 
         int modulo100 = year % 100; 
         //http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm 
         if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) { 
          //its a leap year 
          return day < 30; 
         } else { 
          return day < 29; 
         } 
        default: 
         break; 
        } 
       } 
      } 
      return false; 
     } 

     public String getRegex() { 
      return pattern.pattern(); 
     } 
    } 
} 

Algunas notas útiles:
- para permitir que las afirmaciones (valer cheques) que necesita para utilizar - un argumento cuando se ejecuta el probador. (En Eclipse esto se hace mediante la edición de la configuración Ejecutar/Depurar -> pestaña Argumentos -> Argumentos VM -> insertar "-ea"
- la expresión regular anterior está limitada a los años 1800 a 2199
- que no es necesario use^al principio y $ al final para hacer coincidir solo la cadena de fecha completa. The String.matches se encarga de eso.
- asegúrese de verificar los casos válidos y no válidos y cámbielos de acuerdo con las reglas que tenga.
- la versión "única cadena completa" de cada expresión regular da la misma velocidad que la versión "normal" (la que no tiene^y $). Si observa diferencias de rendimiento, esto se debe a que java "se acostumbra" a procesar las mismas instrucciones el tiempo disminuye. Si cambia las líneas donde se ejecutan las versiones "normal" y "única cadena completa", verá esto probado.

Espero que esto ayude a alguien!
Saludos,
Déspota

+1

http://stackoverflow.com/questions/1905551/how-can-i-make-simpledateformat-parse-fail-when-month-is-greater-than-12 – BalusC

+0

Hey BalusC - eso es cierto:), puede hacer que SimpleDateFormat.parse cubra la mayoría de las fechas no válidas. Ya tuve este enfoque probado en la sección "Comprobación de fecha/prueba y prueba de rendimiento". No cubre fechas inválidas como 001-0002-00003. (Supongo que depende de lo que considere no válido, en mi caso, esta era una fecha no válida). Además, como puede ver, este enfoque es el más lento (consulte la publicación cuando analice el tercer enfoque). Además, puede ejecutar el comprobador a su lado y ver los horarios;) – despot

+0

, también otra sugerencia: si desea hacer la cobertura de expresiones regulares años entre 1900 y 9999, entonces este debería ser el tercer grupo: (([^ 01] [0-9] | 19 | [2-9] [0-9]) \\ d \\ d) – despot

0

la manera apropiada (y fácil) para hacer la validación de fecha/hora utilizando Java 8+ es utilizar la clase java.time.format.DateTimeFormatter. Usar una expresión regular para la validación no es realmente ideal para las fechas. Para el caso de ejemplo en esta pregunta:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 

try { 
    LocalDate date = formatter.parse(text, LocalDate::from); 
} catch (DateTimeParseException e) { 
    // Thrown if text could not be parsed in the specified format 
} 

Este código será analizar el texto, validar que es una fecha válida, y también vuelven la fecha como un objeto LocalDate. Tenga en cuenta que la clase DateTimeFormatter tiene varios formatos de fecha predefinidos estáticos que coinciden con los estándares ISO si su caso de uso coincide con alguno de ellos.