Fastest way to tell if a string is a valid date

前端 未结 8 1170
故里飘歌
故里飘歌 2020-12-13 04:52

I am supporting a common library at work that performs many checks of a given string to see if it is a valid date. The Java API, commons-lang library, and JodaTime all have

8条回答
  •  余生分开走
    2020-12-13 05:20

    You can revert your thinking - try to fail as quickly as possible when the String definitely is no date:

    • it's null
    • its length is not 8 (based on your example date format!)
    • it contains anything other that a number (if your date format is only for numerical dates)

    If none of those apply, then try to parse it - preferably with a pre-made static Format object, don't create one on every method run.


    EDIT after comments

    Based on this neat trick, I wrote a fast validation method. It looks ugly, but is significantly faster than the usual library methods (which should be used in any standard situation!), because it relies on your specific date format and does not create a Date object. It handles the date as an int and goes on from that.

    I tested the daysInMonth() method just a little bit (the leap year condition taken from Peter Lawrey), so I hope there's no apparent bug.

    A quick (estimated!) microbenchmark indicated a speedup by a factor of 30.

    public static boolean isValidDate(String dateString) {
        if (dateString == null || dateString.length() != "yyyyMMdd".length()) {
            return false;
        }
    
        int date;
        try {
            date = Integer.parseInt(dateString);
        } catch (NumberFormatException e) {
            return false;
        }
    
        int year = date / 10000;
        int month = (date % 10000) / 100;
        int day = date % 100;
    
        // leap years calculation not valid before 1581
        boolean yearOk = (year >= 1581) && (year <= 2500);
        boolean monthOk = (month >= 1) && (month <= 12);
        boolean dayOk = (day >= 1) && (day <= daysInMonth(year, month));
    
        return (yearOk && monthOk && dayOk);
    }
    
    private static int daysInMonth(int year, int month) {
        int daysInMonth;
        switch (month) {
            case 1: // fall through
            case 3: // fall through
            case 5: // fall through
            case 7: // fall through
            case 8: // fall through
            case 10: // fall through
            case 12:
                daysInMonth = 31;
                break;
            case 2:
                if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
                    daysInMonth = 29;
                } else {
                    daysInMonth = 28;
                }
                break;
            default:
                // returns 30 even for nonexistant months 
                daysInMonth = 30;
        }
        return daysInMonth;
    }
    

    P.S. Your example method above will return true for "99999999". Mine will only return true for existent dates :).

提交回复
热议问题