为什么SimpleDateFormat解析不正确的日期?

2022-09-01 03:38:07

我有字符串格式的日期,我想将其解析为util date。

var date ="03/11/2013"

我将其解析为:

new SimpleDateFormat("MM/dd/yyyy").parse(date)

但奇怪的是,如果我传递“03-08-201309 hjhkjhk”或“03-88-2013”或43-88-201378“,它不会抛出错误,它会解析它。

现在,我必须编写正则表达式模式来检查日期输入是否正确。但是为什么会这样?

法典:

scala> val date="03/88/201309 hjhkjhk"
date: java.lang.String = 03/88/201309 hjhkjhk

scala> new SimpleDateFormat("MM/dd/yyyy").parse(date)
res5: java.util.Date = Mon May 27 00:00:00 IST 201309

答案 1

您应该使用:DateFormat.setLenient(false)

SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
df.setLenient(false);
df.parse("03/88/2013"); // Throws an exception

我不确定这是否会捕获您想要的所有内容 - 我似乎记得即使它比您想象的更宽松 - 但它应该捕获无效的月份数字。setLenient(false)

我不认为它会捕获尾随文本,例如“03/01/2013 sjsjsj”。您可以使用其接受 的重载,然后在解析完成后检查当前解析索引:parseParsePosition

ParsePosition position = new ParsePosition(0);
Date date = dateFormat.parse(text, position);
if (position.getIndex() != text.length()) {
    // Throw an exception or whatever else you want to do
}

您还应该看看Joda Time API,它很可能允许更严格的解释 - 并且无论如何都是一个通常更清晰的日期/时间API。


答案 2

Jon Skeet的答案是正确的,在2013年写成时是一个很好的答案。

但是,您在问题中使用的类和现在早已过时,因此,如果有人今天遇到类似的问题,恕我直言,最好的答案是更改为使用现代Java日期和时间APISimpleDateFormatDate

很抱歉,我不能写Scala代码,所以你必须忍受Java。我正在使用

private static DateTimeFormatter parseFormatter
        = DateTimeFormatter.ofPattern("MM/dd/yyyy");

格式模式字母与您的问题中相同,但含义略有不同。 从字面上理解图案字母的数量,正如我们将看到的。现在我们尝试:DateTimeFormatter

        System.out.println(LocalDate.parse(date, parseFormatter));

结果:

  • "03/11/2013"按预期解析为。我使用了现代类,一个表示没有时间的日期的类,正是我们在这里需要的。2013-03-11LocalDate
  • 传递给出带有消息的 。非常精确,不是吗?不过,现代API有方法可以只解析字符串的一部分,如果这是我们想要的。"03/88/2013 hjhkjhk"DateTimeParseExceptionText '03/88/2013 hjhkjhk' could not be parsed, unparsed text found at index 10
  • "03/88/201309"给。我们要求一个4位数的年份,并给了它6位数,这导致了反对意见。显然,在尝试将88解释为月份的某一天之前,它会检测到并报告此错误。Text '03/88/201309' could not be parsed at index 6
  • 不过,它也反对每月88的某一天:给出.再次,请享受消息的信息量。"03/88/2013"Text '03/88/2013' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 88
  • "03-08-2013"(用连字符代替斜杠)给出,不是很令人惊讶。索引 2 是第一个连字符所在的位置。Text '03-08-2013' could not be parsed at index 2

Jon Skeet解释说,过时的可以宽大或宽松。这也是正确的,事实上它有3种而不是2种旋转变压器样式,称为“宽松”,“智能”和“严格”。但是,由于许多程序员没有意识到这一点,我认为他们做出了一个很好的选择,不让“宽松”成为默认值(“聪明”是)。SimpleDateFormatDateTimeFormatter

如果我们想让格式化程序宽松呢?

private static DateTimeFormatter parseFormatter
        = DateTimeFormatter.ofPattern("MM/dd/yyyy")
                .withResolverStyle(ResolverStyle.LENIENT);

现在它还将 解析为 .我相信这是老班级也会做的:从3月初算起88天,得出5月27日。其他错误消息仍然相同。换句话说,它仍然反对未解析的文本,6位数字的年份和连字符。"03/88/2013"2013-05-27

问:我可以在 Java 版本中使用现代 API 吗?

如果至少使用Java 6,则可以。

  • 在Java 8及更高版本中,新的API是内置的。
  • 在Java 6和7中,获得ThreeTen Backport,即新类的向后移植(这是JSR-310的ThreeTen,其中首先定义了现代API)。
  • 在 Android 上,使用 Android 版本的 ThreeTen Backport。它被称为ThreeTenABP,我认为这个问题有一个很好的解释:如何在Android项目中使用ThreeTenABP

推荐