我在工作中支持一个公共库,它对给定字符串执行许多检查,以查看它是否是有效日期.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 绝对没有日期时尽量尝试失败:
nulllength不是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 :).
我认为了解某个日期是否有效的更好方法是定义如下方法:
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。