2010-01-27 4 views
39

Je me demande simplement s'il existe un moyen (peut-être avec regex) de valider qu'une entrée sur une application de bureau Java est exactement une chaîne formée comme suit: "AAAA-MM-JJ".Validation du format de date Regex sur Java

J'ai cherché mais sans succès.

Merci

+6

Voulez-vous autoriser toute année, ainsi que les numéros non valides pour les mois et les jours? 9999-99-99 par exemple? Qu'en est-il des dates non valides comme 2009-02-29? –

+0

Question connexe, Regex uniquement: http: // stackoverflow.com/q/8647893/8384 – McKay

Répondre

62

Utilisez l'expression régulière suivante:

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

comme dans

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

Avec la méthode matches, les ancres ^ et $ (début et fin de chaîne, respectivement) sont présents implicitement.

+3

Mais cela ne valide que le format de la date et non la date réelle est valide ou non. J'aime l'idée de @Steve B plus ou si vous vous souvenez correct, il devrait y avoir une bonne validation disponible dans le paquet de validation des communs aussi. –

+2

Les dates sont difficiles. Bien que vous puissiez vérifier le FORMAT de l'entrée, vous ne pouvez pas vérifier le CONTENU avec une simple regex. Nous vous recommandons d'utiliser un analyseur construit pour la tâche. –

+0

@ChrisNava recommandé, oui. Il est possible de faire une validation de date en regex. Mais pas recommandé. http://stackoverflow.com/q/8647893/8384 – McKay

29

Vous avez besoin de plus d'un regex, par exemple "9999-99-00" n'est pas une date valide. Il existe une classe SimpleDateFormat conçue pour cela. Plus lourd, mais plus complet.

par exemple.

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

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

Malheureusement, SimpleDateFormat est à la fois des poids lourds et non thread-safe.

+15

n'oubliez pas d'appeler 'setLenient (false)' si vous voulez que SimpleDateFormat rejette les dates invalides. Votre code acceptera "9999-99-00" (serait "mer février 28 00:00:00 CET 10007") –

+1

Merci Carlos, je ne savais même pas qu'il reconnaîtra les entrées comme 2009-20-20. Merci :) – Sheldon

+0

et un autre point: SimpleDateFormat ne vérifie pas le format: "2010-1-8", "10-001-002", ... sera accepté –

2

Construire un SimpleDateFormat avec le masque, puis appelez: SimpleDateFormat.parse (String, ParsePosition p)

+0

Vous pouvez simplement appeler la méthode à 1 argument 'parse (String)' à la place de la méthode à 2 arguments. _ _ _ Et n'oubliez pas d'appeler 'setLenient (false)' si vous voulez que SimpleDateFormat rejette les dates invalides. –

1

Pour un contrôle précis, envisager une InputVerifier utilisant le SimpleDateFormat("YYYY-MM-dd") suggéré par Steve B.

22

Mettre tous ensemble:

  • REGEX ne valide pas les valeurs (comme "2010-19-19")
  • SimpleDateFormat ne vérifie pas le format ("2010-1-2", "1-0002-003" sont acceptés)

il est nécessaire d'utiliser les deux pour valider le format et la valeur:

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; 
    } 
} 



Un ThreadLocal peut être utilisé pour éviter la création d'un nouveau SimpleDateFormat pour chaque appel.
Il est nécessaire dans un contexte multithread puisque le SimpleDateFormat est pas thread-safe:

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; 
    } 
} 

(même chose peut être fait pour un matcher, qui est pas thread aussi sûr)

+0

regex peut valider les valeurs – McKay

11

Cela fera regex: "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$" Ceci prendra en charge les formats valides et les dates valides. Il ne validera pas les bons jours du mois, c'est-à-dire l'année bissextile.

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")); 

Bonne chance!

7

Je voudrais aller avec un simple regex qui va vérifier que les jours ne pas avoir plus de 31 jours et des mois au plus 12. Quelque chose comme:

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

C'est le format « dd-MM- yyyy ". Vous pouvez le modifier à vos besoins (par exemple enlever le? Pour faire le 0 requis - maintenant c'est facultatif), puis utiliser une logique personnalisée pour réduire les règles spécifiques comme les années bissextiles Février nombre de jours cas, et d'autres mois nombre de jours cas. Voir le code DateChecker ci-dessous.

Je choisis cette approche depuis que j'ai testé que c'est la meilleure quand la performance est prise en compte. J'ai vérifié cette approche (1ère) versus 2ème approche de validation d'une date par rapport à une regex qui s'occupe des autres cas d'utilisation, et 3ème approche d'utiliser la même regex simple ci-dessus en combinaison avec SimpleDateFormat.parse (date).
La 1ère approche était 4 fois plus rapide que la 2ème approche, et 8 fois plus rapide que la 3ème approche. Voir le vérificateur de date autonome et la classe principale du testeur de performance en bas. Une chose que je n'ai pas vérifiée est l'approche (s) de temps joda. (La bibliothèque de date/heure plus efficace).

Date Code du vérificateur:

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(); 
    } 
} 

Date des essais de vérification/test et performance:

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(); 
     } 
    } 
} 

Quelques notes utiles:
- pour permettre aux assertions (faire valoir les contrôles), vous devez utiliser - un argument lors de l'exécution du testeur. (En éclipse, ceci est fait en éditant la configuration Exécution/Débogage -> onglet Arguments -> arguments VM -> insérer "-ea"
- l'expression ci-dessus est limitée aux années 1800 à 2199
- vous n'avez pas besoin de utilisez^au début et $ à la fin pour correspondre uniquement à la chaîne de date entière.Le String.matches s'en charge
- Assurez-vous que vous cochez les cases valides et invalides et modifiez-les en fonction des règles que vous avez
- la version "only full string" de chaque regex donne la même vitesse que la version "normale" (celle sans^et $) Si vous voyez des différences de performances c'est parce que java "s'habitue" au traitement des mêmes instructions Si vous changez les lignes où s'exécutent les versions "normal" et "only whole string", vous le constaterez.

Espérons que cela aide quelqu'un!
Cheers,
Despot

+1

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

+0

Hey BalusC - c'est vrai:), vous pouvez faire en sorte que SimpleDateFormat.parse couvre la plupart des dates invalides. J'ai déjà fait tester cette approche dans la section "vérification de la date/tests et tests de performance". Il ne couvre pas les dates non valides comme 001-0002-00003. (Je suppose que cela dépend de ce que vous considérez invalide - dans mon cas, c'était une date invalide). Aussi, comme vous pouvez le voir, cette approche est la plus lente (consultez le post quand je parle de la 3ème approche). Aussi vous êtes libre d'exécuter le testeur à vos côtés et voir les temps;) – despot

+0

également un autre indice - si vous voulez faire la couverture regex années de 1900 à 9999, que cela devrait être le 3ème groupe: (([^ 01] [0-9] | 19 | [2-9] [0-9]) \\ d \\ d) – despot

0

Le bon (et facile) façon de faire la validation de date/heure à l'aide de Java 8+ est d'utiliser la classe java.time.format.DateTimeFormatter. Utiliser une regex pour la validation n'est pas vraiment idéal pour les dates. Pour le cas d'exemple dans cette question:

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 
} 

Ce code va analyser le texte, valider qu'il est une date valide, et aussi retourner la date comme un objet LocalDate. Notez que la classe DateTimeFormatter comporte un certain nombre de formats de date prédéfinis statiques correspondant aux normes ISO si votre cas d'utilisation correspond à l'un d'entre eux.