判断字符串是否为有效日期的最快方法

jja*_*man 29 java

我在工作中支持一个公共库,它对给定字符串执行许多检查,以查看它是否是有效日期.Java API,commons-lang库和JodaTime都有方法可以解析字符串并将其转换为日期,让你知道它是否真的是一个有效的日期,但我希望有一种方法在没有实际创建日期对象的情况下进行验证(或者像JodaTime库那样使用DateTime).例如,这是一段简单的示例代码:

public boolean isValidDate(String dateString) {
    SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
    try {
        df.parse(dateString);
        return true;
    } catch (ParseException e) {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎很浪费,我们扔掉了最终的对象.从我的基准测试中,我们在这个公共图书馆中有大约5%的时间用于验证日期.我希望我只是错过了一个明显的API.任何建议都会很棒!

UPDATE

假设我们始终可以使用相同的日期格式(可能是yyyyMMdd).我确实考虑过使用正则表达式,但是它需要知道每个月的天数,闰年等等......


结果

解析了一千万次的约会

Using Java's SimpleDateFormat: ~32 seconds 
Using commons-lang DateUtils.parseDate: ~32 seconds
Using JodaTime's DateTimeFormatter: ~3.5 seconds 
Using the pure code/math solution by Slanec: ~0.8 seconds 
Using precomputed results by Slanec and dfb (minus filling cache): ~0.2 seconds
Run Code Online (Sandbox Code Playgroud)

有一些非常有创意的答案,我很感激!我想现在我只需要决定我需要多少灵活性,我希望代码看起来像.我要说dfb的答案是正确的,因为它纯粹是最快的,这是我原来的问题.谢谢!

dfb*_*dfb 15

如果您真的关心性能并且日期格式非常简单,只需预先计算所有有效字符串并将其散列在内存中.您上面的格式在2050年之前只有大约800万个有效组合


由Slanec编辑 - 参考实施

此实现取决于您的特定日期格式.它可以适应任何特定的日期格式(就像我的第一个答案,但更好一点).

它创建了一组dates从1900年到2050年(存储为字符串 - 其中有54787个),然后将给定日期与存储的日期进行比较.

一旦dates创建了集合,它就像地狱一样快.快速微基准测试显示,与我的第一个解决方案相比,改进了10倍.

private static Set<String> dates = new HashSet<String>();
static {
    for (int year = 1900; year < 2050; year++) {
        for (int month = 1; month <= 12; month++) {
            for (int day = 1; day <= daysInMonth(year, month); day++) {
                StringBuilder date = new StringBuilder();
                date.append(String.format("%04d", year));
                date.append(String.format("%02d", month));
                date.append(String.format("%02d", day));
                dates.add(date.toString());
            }
        }
    }
}

public static boolean isValidDate2(String dateString) {
    return dates.contains(dateString);
}
Run Code Online (Sandbox Code Playgroud)

PS它可以被修改为使用Set<Integer>甚至是Trove,TIntHashSet它可以大大减少内存使用量(因此允许使用更大的时间跨度),然后性能下降到原来的解决方案之下.


Pet*_*ček 12

您可以恢复您的想法 - 当String 绝对没有日期时尽量尝试失败:

  • 它的 null
  • length不是8(基于您的示例日期格式!)
  • 包含任何其他数字(如果您的日期格式仅用于数字日期)

如果这些都不适用,那么尝试解析它 - 最好使用预先制作的静态Format对象,不要在每个方法运行中创建一个.


评论后编辑

基于这个巧妙的技巧,我写了一个快速验证方法.它看起来很难看,但是比通常的库方法(应该在任何标准情况下都使用它)快得多,因为它依赖于你的特定日期格式而不会创建一个Date对象.它将日期作为一个处理int并从中继续.

我测试了这个daysInMonth()方法(闰年条件取自Peter Lawrey),所以我希望没有明显的错误.

快速(估计!)微基准标记表示加速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;
}
Run Code Online (Sandbox Code Playgroud)

PS上面的示例方法将返回true"99999999".我只会在现有日期返回true :).


vic*_*dez 6

我认为了解某个日期是否有效的更好方法是定义如下方法:

public static boolean isValidDate(String input, String format) {
    boolean valid = false;

    try {
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        String output = dateFormat.parse(input).format(format);
        valid = input.equals(output); 
    } catch (Exception ignore) {}

    return valid;
}
Run Code Online (Sandbox Code Playgroud)

该方法一方面检查日期格式是否正确,另一方面检查日期是否与有效日期相对应。比如日期“2015/02/29”会被解析为“2015/03/01”,所以输入和输出会不一样,方法会返回false。