意外地将电话号码字符串传递到 SimpleDateFormat 的格式方法中有时会导致解析有效日期。
例如,传递数字"518-123-4567"(文字,带连字符)以某种方式导致日期"11/23/0517 05:27 AM"
我们在字符串可以表示许多不同事物的区域中使用它,并且我们假设以电话号码通常编写的方式使用数字和连字符的字符串在解析为日期时会失败。 我们的代码只是检查 ParseException,并接受任何不会抛出此类异常的有效内容。 为什么这种字符串解析不失败? 有没有更好的方法来检查字符串是否可能是日期?
private static Date getPromisedDate(String promisedText) {
SimpleDateFormat promsiedDateTimeFormat = new SimpleDateFormat("yyyyMMddHHmm");
if(null != promisedText) {
try {
return promsiedDateTimeFormat.parse(promisedText);
}
catch (ParseException e) { }
}
return null;
}
您的SimpleDateFormat
处于"宽松"模式 - 这确实非常宽松。如果您使用
promsiedDateTimeFormat.setLenient(false);
当您尝试解析虚假数据时,它会引发异常。
我个人认为默认情况下应该严格,但是...
从 DateFormat#parse(String)
:
分析给定字符串开头的文本以生成日期。该方法不能使用给定字符串的整个文本。
因此,该方法可能不会分析整个字符串。它将停在模式停止匹配的位置。在您的情况下,匹配通过以下方式完成:
yyyy MM dd HH mm
518 -1 23 -4 567
解析yyyy
的年份在第一-
停止,因为它不能解析为年份。所以,这一年是518
.然后月份作为-1
,然后23
按照dd
,依此类推。
您可以使用parse
方法的重载版本并传递ParsePosition
实例以查看详细信息。
从DateFormat#parse(String, ParsePosition)
:
默认情况下,解析是宽松的:如果输入不是此对象的 format 方法使用的形式,但仍可以解析为日期,则解析成功。客户可以通过调用setLenient(false)来坚持严格遵守格式
因此,只需将宽大设置为false
,以阻止其解析与格式不匹配的日期:
promsiedDateTimeFormat.setLenient(false);
<小时 />例如,在使用 ParsePosition
时,假设您将日期字符串传递为 - "518-123-4567abc"
。令人惊讶的是,它也会在宽大设置为 true
的情况下被解析,因为最后一部分abc
根本不会被解析。要对此进行测试,您可以尝试以下代码:
private static Date getPromisedDate(String promisedText) throws Exception {
ParsePosition pp = new ParsePosition(0);
SimpleDateFormat promsiedDateTimeFormat = new SimpleDateFormat("yyyyMMddHHmm");
if(null != promisedText) {
try {
Date date = promsiedDateTimeFormat.parse(promisedText);
// If complete string is not parsed, throw ParseException
if (pp.getIndex() != promisedText.length()) {
throw new ParseException("Unparseable date given: " + promisedText, pp.getIndex());
}
return date;
}
catch (ParseException e) { throw e; }
}
return null;
}
解释发生了什么:581年,月-1,第23天,小时-4,分钟567。将所有内容汇总,您将获得结果日期。要获得这样的结果,请参阅JonSkeet的帖子