使用SimpleDateFormat
将字符串解析为日期时,我遇到了一种非常奇怪的行为。考虑以下单元测试:
@Test
public void testParse() throws ParseException
{
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String dateStr = "2012-12-21";
Date parsedDate = dateFormat.parse(dateStr);
Calendar date = Calendar.getInstance();
date.setTime(parsedDate);
Assert.assertEquals(2012, date.get(Calendar.YEAR));
Assert.assertEquals(11, date.get(Calendar.MONTH)); // yeah, Calendar sucks
Assert.assertEquals(21, date.get(Calendar.DAY_OF_MONTH));
}
可以看出上面的代码中有一个故意的错误:SimpleDateFormat
初始化为"yyyyMMdd"
,但要解析的字符串格式为{ {1}}。我希望这样的事情导致"yyyy-MM-dd"
,或者至少在尽力而为的基础上正确解析。相反,由于一些奇怪的原因,日期被解析为ParseException
。嗯?!
这是不可接受的,因为处理输入时的一个错误会导致完全意外/毁灭性的事情。在此期间切换到JodaTime,但是理解那里出了什么问题会很好。
答案 0 :(得分:12)
public void setLenient(boolean lenient)
指定日期/时间解析是否宽松。通过宽松的解析,解析器可以使用启发式来解释与该对象的格式不完全匹配的输入。
通过严格的解析,输入必须与此对象的格式匹配。
如果您将其设置为false,则会获得ParseException
答案 1 :(得分:4)
如果使用DateFormat.parse()
函数,则字符串必须满足输入格式。如果不这样做,解析函数解析错误。这里有关于javaDoc的评论:
默认情况下,解析是宽松的:如果输入不是使用的形式 通过这个对象的格式方法,但仍然可以解析为日期 解析成功了。客户可能会坚持严格遵守 通过调用setLenient(false)格式化。
然后,添加setLenient(false)
行将解决您的问题。在这种情况下,Java会抛出异常。
答案 2 :(得分:2)
那么,输入将分为3个组成部分:年,月,日,你得到 month = -12 和 day = -21 (用于更正)见下文)。尝试解析2012/12/21
,你会得到例外:)
编辑:摘自JavaDoc:
月份:如果模式字母的数量为3或更多,则将月份解释为文本;否则,它被解释为数字。
Edit2:更正
查看SimpleDateFormat
的来源,似乎2012-12-21
实际上已分为:
year = "2012"
month = "-1"
day = "2-"
源注释表明数字后面的-
可能表示负数(取决于区域设置)或是分隔符。在你的情况下,它似乎被视为分隔符,因此day = "2-"
导致day = 2
,因此11月的秒。
答案 3 :(得分:1)
您需要调用setLenient(false)。默认值为true,Java尝试转换字符串,即使它与100%不匹配。