java.text.simpleDataFormat中的奇怪行为应为yyyyMMdd给定yyyy MM dd

java.text.simpleDataFormat中的奇怪行为应为yyyyMMdd给定yyyy MM dd,java,simpledateformat,Java,Simpledateformat,在使用SimpleDateFormat将字符串解析为日期时,我遇到了一个非常奇怪的行为。考虑下面的单元测试: @Test public void testParse() throws ParseException { DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); String dateStr = "2012-12-21"; Date parsedDate = dateFormat.parse(date

在使用
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
初始化为
“yyyyymmdd”
,但要分析的字符串格式为
“yyyy-MM-dd”
。我希望这样的事情会导致一个
ParseException
,或者至少在尽最大努力的基础上正确地解析。相反,由于某种奇怪的原因,日期被解析为
2011-11-02
。嗯


这是不可接受的,因为处理输入时的一个错误将导致完全出乎意料/毁灭性的结果。同时切换到JodaTime,但如果能了解出了什么问题,那就太好了。

好吧,输入将分为3个部分:年、月、日,您将得到月=-12和日=-21(更正请参见下文)。尝试解析
2012/12/21
,您将得到异常:)

编辑:摘自JavaDoc:

月份:如果模式字母的数量为3个或更多,则将月份解释为文本;否则,它将被解释为一个数字

编辑2:更正

查看
SimpleDateFormat
的源代码,似乎
2012-12-21
实际上分为以下几部分:

year = "2012"
month = "-1"
day = "2-" 
源注释指出,数字后面的
-
可能表示负数(取决于区域设置)或是分隔符。在您的情况下,它似乎被视为一个分隔符,因此
day=“2-”
导致
day=2
,因此是11月2日。

摘自:

public void setLenient(布尔lenient)

指定是否放宽日期/时间分析。通过宽松的解析,解析器可以使用启发式来解释与此对象格式不完全匹配的输入。
通过严格解析,输入必须与此对象的格式匹配


如果将其设置为false,您将得到
ParseException

您需要调用setLenient(false)。默认值为true,Java会尝试转换字符串,即使它与100%不匹配。

如果使用
DateFormat.parse()
函数,字符串必须满足输入格式。如果不这样做,则解析函数parse错误。以下是javaDoc中对此的评论:

默认情况下,解析是宽松的:如果输入不是使用的形式 通过此对象的format方法,但仍然可以解析为日期,然后 解析成功。客户可能会坚持严格遵守 通过调用setLenient设置格式(false)


然后,添加
setLenient(false)
行可以解决您的问题。在本例中,Java抛出异常。

哇,感谢您的解释!我仍然不明白为什么-12个月是11月,为什么-21天是11月的第8天。我想这是一种下溢?我在文档的任何地方都没有看到它的描述。@Daniel内部
SimpleDateFormat
使用
Calendar
,正如其他人已经建议的那样,默认情况下是宽松的,因此会调整传入的负值。感谢您解开这个谜!我提名
lenient==true
为20st世纪最佳默认值奖(是的,它是在1997年9月12日发布的Java1.1.4中)。接下来,我投票将
lenient
添加到,以便在添加(而不是删除)元素时使用适当的启发式方法。