2010-01-27 87 views
39

Tôi chỉ tự hỏi nếu có cách nào (có thể với regex) để xác thực rằng đầu vào trên ứng dụng máy tính để bàn Java chính xác là một chuỗi được định dạng là: "YYYY-MM-DD".Xác thực định dạng ngày tháng Regex trên Java

Tôi đã tìm kiếm nhưng không thành công.

Cảm ơn bạn

+6

Bạn có muốn cho phép bất kỳ năm nào cũng như số không hợp lệ trong tháng và ngày không? 9999-99-99 chẳng hạn? Điều gì về các ngày không hợp lệ như 2009-02-29? –

+0

Câu hỏi liên quan, chỉ Regex: http: // stackoverflow.com/q/8647893/8384 – McKay

Trả lời

62

Sử dụng biểu thức chính quy sau:

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

như trong

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

Với phương pháp matches, neo ^$ (bắt đầu và kết thúc của chuỗi, tương ứng) có mặt hoàn toàn.

+3

Nhưng điều này chỉ xác thực định dạng ngày không phải là ngày thực tế hợp lệ hay không. Tôi thích ý tưởng của @Steve B nhiều hơn hoặc nếu nhớ chính xác, cần có một xác nhận tốt có sẵn trong gói xác thực commons quá. –

+2

Ngày khó. Mặc dù bạn có thể xác minh FORMAT của đầu vào nhưng bạn không thể xác minh CONTENT bằng một regex đơn giản. Đề xuất bạn sử dụng trình phân tích cú pháp được tạo cho tác vụ. –

+0

@ChrisNava được đề xuất, có. Có thể thực hiện xác nhận ngày trong regex. Nhưng không được khuyến khích. http://stackoverflow.com/q/8647893/8384 – McKay

29

Bạn cần nhiều hơn regex, ví dụ "9999-99-00" không phải là ngày hợp lệ. Có một lớp học SimpleDateFormat được xây dựng để thực hiện việc này. Nặng hơn, nhưng toàn diện hơn.

ví dụ:

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

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

Thật không may, SimpleDateFormat vừa nặng vừa không an toàn.

+15

đừng quên gọi 'setLenient (false)' nếu bạn muốn SimpleDateFormat từ chối ngày không hợp lệ. Mã của bạn sẽ chấp nhận "9999-99-00" (sẽ là 'Wed Feb 28 00:00:00 CET 10007') –

+1

Cảm ơn bạn Carlos, tôi thậm chí không biết rằng nó sẽ nhận ra đầu vào như 2009-20-20. Cảm ơn :) – Sheldon

+0

và một điểm khác: SimpleDateFormat không kiểm tra định dạng: "2010-1-8", "10-001-002", ... sẽ được chấp nhận –

2

Xây dựng một SimpleDateFormat với mặt nạ, và sau đó gọi: SimpleDateFormat.parse (String s, ParsePosition p)

+0

Bạn có thể chỉ cần gọi phương thức 1 đối số là "phân tích cú pháp (String)' thay vì phương thức 2 đối số. _ _ _ Và đừng quên gọi 'setLenient (false)' nếu bạn muốn SimpleDateFormat từ chối ngày không hợp lệ. –

1

Để kiểm soát tốt, hãy xem xét một InputVerifier sử dụng SimpleDateFormat("YYYY-MM-dd") đề xuất bởi Steve B.

22

Đưa nó tất cả cùng nhau:

  • REGEX không xác nhận giá trị (như "2010-19-19")
  • SimpleDateFormat không kiểm tra định dạng ("2010/01/02", "1-0002-003" được chấp nhận)

đó là cần thiết để sử dụng cả hai để xác định dạng và giá trị:

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



Một ThreadLocal có thể được sử dụng để tránh việc tạo ra một SimpleDateFormat mới cho mỗi cuộc gọi.
Nó là cần thiết trong bối cảnh multithread kể từ khi SimpleDateFormat không phải là thread an toàn:

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

(tương tự có thể được thực hiện cho một Matcher, đó cũng không phải là thread an toàn)

+0

regex có thể xác thực giá trị – McKay

11

này sẽ làm điều đó regex: "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$" Điều này sẽ xử lý các định dạng hợp lệ và ngày hợp lệ. Nó sẽ không xác nhận ngày chính xác của tháng, tức là năm nhuận.

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

Chúc may mắn!

7

tôi sẽ đi với một regex đơn giản mà sẽ kiểm tra xem ngày không có nhiều hơn 31 ngày và tháng không quá 12. Một cái gì đó như:

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

Đây là định dạng "dd-MM- yyyy ". Bạn có thể tinh chỉnh nó theo nhu cầu của bạn (ví dụ cất cánh? Để làm cho 0 hàng đầu bắt buộc - bây giờ là tùy chọn), và sau đó sử dụng một logic tùy chỉnh để cắt giảm các quy tắc cụ thể như số năm tháng 2 của trường hợp ngày và số tháng trường hợp. Xem mã DateChecker dưới đây.

Tôi đang chọn phương pháp này vì tôi đã kiểm tra rằng đây là cách tốt nhất khi tính đến hiệu suất. Tôi đã kiểm tra phương pháp tiếp cận này (1) so với phương pháp thứ hai để xác thực ngày tháng đối với một trường hợp sử dụng các trường hợp sử dụng khác và cách tiếp cận thứ 3 sử dụng cùng một regex đơn giản ở trên kết hợp với SimpleDateFormat.parse (ngày). Cách tiếp cận thứ nhất nhanh gấp 4 lần so với phương pháp thứ 2 và nhanh gấp 8 lần so với phương pháp thứ 3. Xem trình kiểm tra ngày tự kiểm tra và lớp kiểm tra hiệu suất chính ở dưới cùng. Một điều mà tôi bỏ chọn là cách tiếp cận thời gian joda (s). (Thư viện ngày/giờ hiệu quả hơn).

ngày đang kiểm tra:

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

Ngày kiểm tra/kiểm tra và thực hiện kiểm tra:

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

Một số lưu ý hữu ích:
- để cho phép khẳng định (khẳng định kiểm tra), bạn cần phải sử dụng - đối số ea khi chạy trình kiểm tra. (Trong nhật thực, việc này được thực hiện bằng cách chỉnh sửa cấu hình Run/Debug -> Arguments tab -> VM Arguments -> insert "-ea"
- regex ở trên được giới hạn từ 1800 đến 2199
- bạn không cần phải sử dụng^ở đầu và $ ở cuối để chỉ khớp với toàn bộ chuỗi ngày. Chuỗi String.matches sẽ xử lý điều đó
- đảm bảo bạn kiểm tra các trường hợp hợp lệ và không hợp lệ và thay đổi chúng theo các quy tắc mà bạn có.
- phiên bản "chỉ toàn bộ chuỗi" của mỗi regex cho tốc độ giống với phiên bản "bình thường" (phiên bản không có^và $). Nếu bạn thấy sự khác biệt về hiệu suất, điều này là do java "được sử dụng" để xử lý các hướng dẫn tương tự Nếu bạn chuyển đổi các dòng mà phiên bản "bình thường" và "chỉ toàn bộ chuỗi" thực thi, bạn sẽ thấy điều này đã được chứng minh.

Hy vọng điều này sẽ giúp ai đó!
Chúc mừng,
bạo chúa

+1

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

+0

Hey BalusC - đó là sự thật:), bạn có thể làm cho SimpleDateFormat.parse bao gồm hầu hết các ngày không hợp lệ. Tôi đã có phương pháp này được thử nghiệm trong phần "Kiểm tra ngày/thử nghiệm và kiểm tra hiệu suất". Nó không bao gồm các ngày không hợp lệ như 001-0002-00003. (Tôi đoán nó phụ thuộc vào những gì bạn cho là không hợp lệ - trong trường hợp của tôi đây là một ngày không hợp lệ). Cũng như bạn có thể thấy cách tiếp cận này là chậm nhất (hãy kiểm tra bài viết khi tôi thảo luận về phương pháp thứ 3). Ngoài ra bạn được tự do chạy Trình kiểm tra ở bên cạnh bạn và xem thời gian;) – despot

+0

cũng là một gợi ý khác - nếu bạn muốn tạo năm bao gồm regex từ 1900 đến 9999, thì đây sẽ là nhóm thứ 3: (([^ 01] [0-9] | 19 | [2-9] [0-9]) \\ d \\ d) – despot

0

Cách thích hợp (và dễ dàng) để thực hiện xác nhận ngày/giờ sử dụng Java 8 + là sử dụng lớp java.time.format.DateTimeFormatter. Sử dụng regex để xác thực không thực sự lý tưởng cho các ngày. Đối với trường hợp ví dụ trong câu hỏi này:

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 
} 

Mã này sẽ phân tích các văn bản, xác nhận rằng đó là một ngày hợp lệ, và cũng có thể quay trở lại ngày như một đối tượng LocalDate. Lưu ý rằng lớp DateTimeFormatter có một số định dạng ngày được xác định trước tĩnh phù hợp với tiêu chuẩn ISO nếu trường hợp sử dụng của bạn khớp với bất kỳ trường hợp nào trong số đó.

Các vấn đề liên quan