当前位置: 首页 > 知识库问答 >
问题:

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

诸葛康胜
2023-03-14

我有字符串格式的日期,我想解析成使用日期。

var date ="03/11/2013"

我将其解析为:

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

但奇怪的是,如果我通过"03-08-201309hjhkjhk"或"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

共有2个答案

公羊招
2023-03-14

Jon Skeet的答案是正确的,在2013年写这篇文章时是一个很好的答案。

然而,您在问题中使用的类,SimpleDateFormatDate,现在已经过时很久了,所以如果今天有人遇到类似的问题,恕我直言,最好的答案是改用现代的Java日期

很抱歉,我无法编写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,消息文本“03/88/2013 hjhkjhk”无法解析,未解析文本位于索引10。非常精确,不是吗?不过,如果我们想要解析字符串的一部分,那么现代API有一些方法
  • 无法在索引6处分析文本“03/88/201309”。我们要求一个4位数的年份,并给出了6位数,这导致了反对。显然,在试图将88解释为一个月中的一天之前,它会检测并报告这个错误
  • 它也反对将月份的某一天定为88,但是:“03/88/2013”给出了无法解析的文本“03/88/2013”:DayOfMonth的值无效(有效值1-28/31):88。同样,请享受这条信息的丰富性
  • “03-08-2013”(用连字符而不是斜杠)给出的文本“03-08-2013”无法在索引2处解析,这并不奇怪。索引2是第一个连字符所在的位置

Jon Skeet解释说,过时的简化格式可以是宽松的,也可以是不宽松的。这也适用于DateTimeFormatter,实际上它有3种而不是2种解析器样式,分别称为“lenient”、“smart”和“strict”。但是,由于许多程序员没有意识到这一点,我认为他们做出了一个很好的选择,没有将“宽松”作为默认值(“智能”是)。

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

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吗?

如果使用至少Java6,则可以。

  • 在Java8和更高版本中,新的API是内置的
  • 在Java6和Java7中,获取三个后端口,即新类的后端口(对于JSR-310来说是三个,现代API是在这里首次定义的)
  • 在Android上,使用Android版的Three Ten Backport。它叫做ThreeTenABP,我认为这个问题有一个很好的解释:如何在Android项目中使用ThreeTenABP

戴博
2023-03-14

你应该使用DateFormat。setLenient(假)

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

我不确定它是否能捕捉到你想要的一切——我似乎记得,即使使用setLenient(false)它也比你预期的更宽松——但它应该捕捉到无效的月份数字。

我认为它不会捕捉尾随文本,例如“03/01/2013 SJ”。您可能会使用parse的重载,它接受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。

 类似资料:
  • 问题内容: 我有字符串格式的日期,我想将其解析为使用日期。 我将其解析为: 但是奇怪的是,如果我传递的是“ 03-08- 201309 hjhkjhk ”或“ 03- 88 -2013”​​或 43 -88-201378,它不会抛出错误,而是对其进行解析。 为此,我必须编写正则表达式模式以检查日期的输入是否正确。但是为什么会这样呢? 代码: 问题答案: 您应该使用: 我不确定是否可以捕获 到 您想

  • 问题内容: 我正在尝试将UTC中的字符串格式日期转换为日期对象,这导致转换关闭了几分钟。 解析日期字符串之前- 解析后的日期是 正确的日期转换是 转换之间大约相差12-13分钟。我已经观察到转换在10分钟范围内的差异。 知道出了什么问题吗? 问题答案: 解析的是毫秒数,而不是您期望的毫秒数。 788810毫秒是13分钟,8秒和810毫秒。因此,您的结果实际上是2014-07-07T18:27:31

  • 我尝试将字符串转换为日期。

  • 问题内容: 我有这个代码: 而且我希望dateFormat.parse调用会引发ParseException,因为我提供的年份长5个字符,而不是像我定义的格式那样长4个字符。但是出于某种原因,即使将lenient设置为false,此调用也会返回Date对象10/20/20128。 这是为什么?这对我来说没有多大意义。还有其他设置可以使其更加严格吗? 问题答案: 20128是有效的一年,我想Java

  • 例如,我得到了这个日期字符串: 看起来Android无法解析某些时区。感谢@Burhanuddin Rashid的这种方法。 此处的解决方案:不可更改的日期:“IST 2014年星期五10月10日23:11:07”(偏移量20)

  • 问题内容: SimpleDateFormat: 抛出的异常: 有任何想法吗? 编辑: 感谢您的快速解答。你们都是正确的,我只是错过了SimpleDateFormat文档中的一个关键句子-我可能应该把它称为一天。 问题答案: 从SimpleDateFormat javadocs : 月:如果图案字母的数目为3或更多,则将月份解释为文本;否则,将其解释为数字。 尝试使用“ MMM dd yyyy”之类