39

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

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
4

2 回答 2

73

你应该使用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”。您可能会使用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
}

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

于 2013-03-11T10:33:02.677 回答
11

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

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

很抱歉我不能编写 Scala 代码,所以你将不得不使用 Java。我在用

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

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

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

结果:

  • "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 种解析器样式,称为“宽松”、“智能”和“严格”。但是,由于许多程序员没有意识到这一点,我认为他们做出了一个不错的选择,即不将“lenient”设为默认值(“smart”是)。

如果我们想让我们的格式化程序宽松怎么办?

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

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

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

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

于 2017-07-27T09:37:42.153 回答