为什么SimpleDateFormat.parse这里不抛异常?

Why does SimpleDateFormat.parse not throw an exception here?

首先请注意我在这里坚持 java6。

我正在尝试从可能具有不同格式的字符串中获取日期。 出于某种原因,我没有得到我期望的 ParseException...

    import java.text.SimpleDateFormat;
    import java.util.Date;

    public class test1{

      public static void main(String argc[]){
          System.out.println(parseAllDateFormats(argc[0]));
    }

      private static final String[] dateFormats = "yyyyMMdd,yyyy/MM/dd,yyyy-MM-dd,dd/MM/yyyy,dd-MM-yyyy,dd-MMM-yyyy,yyyy MM dd".split(",");
      public static Date parseAllDateFormats(String date) {
          if (date == null)
              return null;
          for (int f = 0; f < dateFormats.length; f++) {
              String format = dateFormats[f];
              try {
                  SimpleDateFormat dateFormat = new SimpleDateFormat(format);
    System.out.println("trying " + format);                
                  return dateFormat.parse(date);
              }
              catch (Exception e) {}
          }
          return null;
      }

    }

运行: java 测试 1 1980-04-25

我希望得到:

trying yyyyMMdd
trying yyyy/MM/dd
trying yyyy-MM-dd
Fri Apr 25 00:00:00 EST 1980

但我只得到:

trying yyyyMMdd
Tue Dec 04 00:00:00 EST 1979

知道哪里出了问题吗?

SimpleDateFormat 默认情况下是宽松的,即使给定日期的格式可能与其配置为解析的格式不匹配,也不会抛出异常。您可以像这样将其设置得不那么宽松:

SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false);

这样它将检查字符在该日期是否确实有效。

Any idea what's wrong?

这是 SimpleDateFormat 变得非常麻烦的地方之一。当尝试使用模式 yyyyMMdd 解析 1980-04-25 时,它将 1980 解析为预期的年份,然后将 -0 解析为月份,因为它应该是两个字符,然后 4 作为一个月中的某天,即使它也应该是两个字符。它忽略 -25 因为它已经完成解析。

标准设置的SimpleDateFormat也忽略了没有月份-0的事实。它取 0,所以 1980 年 1 月之前的一个月,这就是为什么你得到 1979 年 12 月。

SimpleDateFormat 是出了名的麻烦,而 Date 也设计得很糟糕。两者都已经过时了。我建议你不要使用 SimpleDateFormat。曾经。

java.time

private static final String[] dateFormats
        = "yyyyMMdd,yyyy/MM/dd,yyyy-MM-dd,dd/MM/yyyy,dd-MM-yyyy,dd-MMM-yyyy,yyyy MM dd"
                .split(",");

public static LocalDate parseAllDateFormats(String date) {
    if (date == null) {
        return null;
    }
    for (String format : dateFormats) {
        try {
            System.out.println("trying " + format);                
            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(format, Locale.ENGLISH);
            return LocalDate.parse(date, dateFormatter);
        }
        catch (DateTimeParseException e) {
            // Ignore, try next format
        }
    }
    return null;
}

让我们用您的示例字符串试试看:

    System.out.println(parseAllDateFormats("1980-04-25"));

输出是预期的:

trying yyyyMMdd
trying yyyy/MM/dd
trying yyyy-MM-dd
1980-04-25

我正在使用 java.time,现代 Java 日期和时间 API。与旧的日期时间 类 相比,它更好用。现代 DateTimeFormatter 可以将解析的值解析为三种样式的日期:严格、智能和宽松。 Smart 是默认值,它只接受从 1 到 12 的月份编号。此外 LocalDate.parse 将坚持解析整个字符串,否则会抛出异常。

与您的代码相比,我还做了一些小修改。对于大多数目的,我 制作一个 DateTimeFormatter 的数组(而不是 String),但它不允许我打印 trying yyyyMMdd,所以在这里我没有。我在第一个 if 语句中放置了大括号。我在格式化程序上指定了语言环境,因为不同语言的月份缩写不同,我想控制使用哪种语言。我抓住了特定的 DateTimeParseException 并添加了评论为什么我忽略它因为否则忽略异常是一件坏事,坏事。在方法的底部,如果格式无效而不是返回 null.

,您还应该考虑抛出异常

Java 6

be aware I'm stuck with java6 here.

  • java.time 类 内置于 Java 8 及更高版本,以及 Android 26 及更高版本。
  • ✅ 大多数 java.time 功能已向后移植到 ThreeTen-Backport 中的 Java 6 和 Java 7项目。
  • 进一步改编为 ThreeTenABP. See 中较早的 Android (<26)。

链接

  • 相关问题:SimpleDateFormat giving wrong date instead of error
  • 相关问题:SimpleDateFormat parse(string str) doesn't throw an exception when str = 2011/12/12aaaaaaaaa?
  • Oracle tutorial: Date Time 解释如何使用 java.time.