【问题标题】:I have a problem about SimpleDateFormat function in java我对 java 中的 SimpleDateFormat 函数有疑问
【发布时间】:2021-04-30 08:13:41
【问题描述】:

我不知道为什么没有错误。代码是

String datestr = "2021-01-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
try {
    System.out.println(sdf.parse(datestr));
} catch (ParseException e) {
    e.printStackTrace();
}

由于日期字符串 2021-01-01 与格式模式字符串 yyyy-MM 不匹配,因此我预期并希望出现异常。

控制台打印Fri Jan 01 00:00:00 CST 2021

相反的情况确实会导致预期的异常。 我让代码看起来像这样

        String datestr = "2021-01";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            System.out.println(sdf.parse(datestr));
        } catch (ParseException e) {
            e.printStackTrace();
        }

控制台打印错误消息:Unparseable date: "2021-01"

谁能告诉我为什么?

【问题讨论】:

  • 为什么你用yyyy-MM-dd解析2021-01,而用yyyy-MM解析2021-01-01???我真的很好奇...
  • 我建议你不要使用SimpleDateFormatDate。这些类设计不佳且早已过时,尤其是前者,尤其是出了名的麻烦。而是使用来自java.time, the modern Java date and time APILocalDateYearMonth
  • 这是一个很好的问题。没有理由结束这个问题(说是因为它已经获得了几票赞成票)。
  • 用法:SimpleDateFormat 不是一个函数,它是一个类。虽然在这种情况下你的意思很清楚,但在其他情况下,使用正确和商定的条款会使人们完全了解你的意思,而人们要么不知道,要么更糟,完全误解你的意图。跨度>

标签: java validation simpledateformat date-parsing parseexception


【解决方案1】:

您正在描述两种不同的场景:

  1. 您的输入文本包含 更多 数据,超过了模式将消耗的数据,额外的数据位于末尾。
  2. 您的输入文本所包含的数据少于模式所消耗的数据。

#1 不是错误,因为parse(String) 被明确记录为从输入的开头读取直到它足够:

该方法可能不会使用给定字符串的整个文本。

#2 是一个错误,因为您的模式说它需要额外的数据(即 - 后跟一天)并且数据丢失了。

【讨论】:

    【解决方案2】:

    tl;博士

    您的格式模式必须适合您的输入文本。在第一种情况下,"2021-01-01""yyyy-MM" 不匹配。在第二种情况下,"2021-01""yyyy-MM-dd" 不匹配。

    改用 java.time 类,因为与旧类 SimpleDateFormatDate 较量是不明智的。也更简单,不需要格式化模式。

    YearMonth
    .from( 
        LocalDate.parse( "2021-01-01" ) 
    )
    .toString()
    

    看到这个code run live at IdeOne.com

    2021-01

    并解析年月:

    YearMonth.parse( "2021-01" )
    

    详情

    您正在使用糟糕的日期时间类,这些类在几年前已被现代 java.time 类所取代。 Sun、Oracle 和 JCP 社区一致通过 JSR 310 放弃了这些关闭。

    您的输入恰好使用标准的ISO 8601 格式。 java.time 类在解析/生成文本时默认使用这些格式。所以不需要指定格式模式。

    LocalDate ld = LocalDate.parse( "2021-01-01" ) ;
    YearMonth ym = YearMonth.from( ld ) ;
    String output = ym.toString() ;
    

    至于解析"2021-01"的输入:

    YearMonth ym = YearMonth.parse( "2021-01" ) ;
    

    【讨论】:

    • @JoakimDanielson 很公平,我修改了我的答案,使其更加准确。谢谢你的批评。
    【解决方案3】:

    这是一个适度的补充。 Basil Bourque 已经为您的工作展示了良好且现代的解决方案,而 Generous Badger 已经解释了为什么您的代码没有按照您的预期运行。

    如果您在每个代码示例中输入格式错误的字符串,Basil Bourque 的代码确实会引发预期的异常。对于您所询问的代码的现代版本:

        String datestr = "2021-01-01";
        YearMonth.parse(datestr);
    

    结果是:

    线程“主”java.time.format.DateTimeParseException 中的异常: 无法解析文本“2021-01-01”,在索引 7 处找到未解析的文本

    还请欣赏异常消息的精确度。

    如果您选择使用显式格式化程序,也会发生同样的事情:

        YearMonth.parse(datestr, DateTimeFormatter.ofPattern("uuuu-MM"));
    

    线程“主”java.time.format.DateTimeParseException 中的异常: 无法解析文本“2021-01-01”,在索引 7 处找到未解析的文本

    不要使用 SimpleDateFormat

    SimpleDateFormat 有很多令人困惑且设计不当的特征,您尝试在两个代码示例中使用该类。你只被其中两个击中过:

    1. 正如 Generous Badger 解释的那样,它可能不会解析给它的整个字符串,默认留下未解析的文本。
    2. 它为未解析的字段创建默认值。在您的情况下,它默认为 JVM 的默认时区中的每月 1 日 00:00:00。虽然可以指示 java.time 为不在解析字符串中的字段应用默认值,但这需要在 Java 代码中明确指定它们,从而导致更可预测且更少令人惊讶的行为。

    【讨论】:

      【解决方案4】:

      其他人已经提到了为什么会发生这种情况(java 解析器在获得所需的所有内容时停止解析)并且还解释说这个 API 较旧并且不符合人体工程学。也就是说,您可以使用 ParsePosition 类做您想做的事。

      String datestr = "2021-01-01";
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
      ParsePosition pp = new ParsePosition(0);
      sdf.parse(datestr, pp);
      if(pp.getIndex() < dateStr.length()) {
        throw new ParseException("Parsing failed...");
      }
      

      【讨论】:

      • 感谢您的回答。这是个好主意。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-05
      • 1970-01-01
      • 2022-01-26
      相关资源
      最近更新 更多