java.text.SimpleDateFormat 中的奇怪行为期望 yyyyMMdd 给定 yyyy-MM-dd

发布于 2024-11-26 19:11:37 字数 884 浏览 0 评论 0原文

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

这是不可接受的,因为处理输入时的一个错误就会导致完全意外/毁灭性的结果。同时切换到 JodaTime,但很高兴了解那里出了什么问题。

I have encountered a very weird behavior while using SimpleDateFormat for parsing a string to a date. Consider the following unit test:

@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));
}

As it can be seen there is an intentional mistake in the above code: the SimpleDateFormat is initialized with "yyyyMMdd" but the string to be parsed is in the format "yyyy-MM-dd". I would expect that such thing results in a ParseException, or at least be parsed on a best-effort basis correctly. Instead, for some weird reason the date is parsed as 2011-11-02. Eh?!

This is unacceptable as one single mistake while handling the inputs would result in something totally unexpected / devastating. Switched to JodaTime in the meantime, but it would be nice to understand what went wrong there.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

北方。的韩爷 2024-12-03 19:11:37

摘自 JavaDoc for setLenient

public void setLenient(boolean lenient)

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

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

Extract from JavaDoc for setLenient:

public void setLenient(boolean lenient)

Specify whether or not date/time parsing is to be lenient. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object's format.
With strict parsing, inputs must match this object's format.

If you set it to false, you will get ParseException

独﹏钓一江月 2024-12-03 19:11:37

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

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

然后,添加 setLenient(false) 行即可解决您的问题。在这种情况下,Java 会抛出异常。

If you use the DateFormat.parse() function, the string must satisfy the input format. if it doesn't do it, the parse function parse wrong. Here a comment about this in the javaDoc:

By default, parsing is lenient: If the input is not in the form used
by this object's format method but can still be parsed as a date, then
the parse succeeds. Clients may insist on strict adherence to the
format by calling setLenient(false).

Then, your problem would be fixed adding the setLenient(false) line. In this case, Java throws the exception.

最舍不得你 2024-12-03 19:11:37

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

编辑:摘自JavaDoc:

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

Edit2:更正

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

year = "2012"
month = "-1"
day = "2-" 

源代码注释指出 - > 后面的数字可能表示负数(取决于区域设置)或分隔符。在您的情况下,它似乎被视为分隔符,因此 day = "2-" 结果为 day = 2,因此 second十一月。

Well, the input would be split into 3 components: year, month, day and you'd get month = -12 and day = -21 (for correction see below). Try to parse 2012/12/21 and you'll get the exception :)

Edit: excerpt from the JavaDoc:

Month: If the number of pattern letters is 3 or more, the month is interpreted as text; otherwise, it is interpreted as a number.

Edit2: Correction

Looking at the source of SimpleDateFormat it seems that 2012-12-21 is actually split into this:

year = "2012"
month = "-1"
day = "2-" 

The source comments state that a - following a number might either denote a negative number (depending on the locale) or be a delimiter. In your case it seems to be taken as a delimiter, thus day = "2-" results in day = 2, hence the second of November.

痴者 2024-12-03 19:11:37

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

You need to call setLenient(false). The default is true and Java tries to convert the string even it does not match 100%.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文