为什么SimpleDateFormat解析错误的日期?

Ris*_*shi 33 java scala

我有字符串格式的日期,我想解析它到util日期.

var date ="03/11/2013"
Run Code Online (Sandbox Code Playgroud)

我正在解析这个:

new SimpleDateFormat("MM/dd/yyyy").parse(date)
Run Code Online (Sandbox Code Playgroud)

但奇怪的是,如果我传递"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
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 56

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

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

我不确定会抓住你想要的一切 - 我似乎记得即使setLenient(false)它比你想象的更宽松 - 但它应该抓住无效的月份数字.

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

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
}
Run Code Online (Sandbox Code Playgroud)

您还应该查看Joda Time API,这可能会允许更严格的解释 - 并且无论如何都是一个通常更干净的日期/时间API.


Ole*_*.V. 5

乔恩·斯凯特(Jon Skeet)的答案是正确的,在2013年撰写时是一个很好的答案。

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

抱歉,我无法编写Scala代码,因此您将不得不使用Java。我在用

private static DateTimeFormatter parseFormatter
        = DateTimeFormatter.ofPattern("MM/dd/yyyy");
Run Code Online (Sandbox Code Playgroud)

格式字母与您的问题相同,尽管含义略有不同。DateTimeFormatter如我们将看到的,从字面上接受模式字母的数量。现在我们尝试:

        System.out.println(LocalDate.parse(date, parseFormatter));
Run Code Online (Sandbox Code Playgroud)

结果:

  • "03/11/2013"被2013-03-11按预期解析。我使用了现代LocalDate类,该类表示没有日期的日期,正是我们在这里需要的。
  • 传递"03/88/2013 hjhkjhk"会给出一条DateTimeParseException消息Text '03/88/2013 hjhkjhk' could not be parsed, unparsed text found at index 10。相当精确,不是吗?不过,现代的API具有仅解析字符串部分的方法(如果我们想要的话)。
  • "03/88/201309"给Text '03/88/201309' could not be parsed at index 6。我们要求输入4位数字的年份并给它6位数字,这导致了异议。显然,它会在尝试将88解释为一个月的某天之前检测到并报告此错误。
  • 不过,它也确实反对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"(用连字符而不是斜杠)给出Text '03-08-2013' could not be parsed at index 2,这并不奇怪。索引2是第一个连字符所在的位置。

乔恩·斯凯特(Jon Skeet)解释说,过时的SimpleDateFormat可能是宽容的,也可能是宽容的。这也是正确的DateTimeFormatter,实际上,它具有3种而不是2种解析器样式,分别称为“宽松”,“智能”和“严格”。但是,由于许多程序员并不了解这一点,所以我认为他们做出了一个很好的选择,即不将“宽大”作为默认值(“智能”为默认值)。

如果我们想宽大格式化程序怎么办?

private static DateTimeFormatter parseFormatter
        = DateTimeFormatter.ofPattern("MM/dd/yyyy")
                .withResolverStyle(ResolverStyle.LENIENT);
Run Code Online (Sandbox Code Playgroud)

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

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

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