Java 使用点“.”时无法解析日期。而不是破折号“-”

Java unable to parse date when using dots "." instead of dashes "-"

我有一个令人困惑的问题,考虑这个工作代码:

package com.mycompany.mavenproject2;

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

public class Main {
  public static void main(String[] args) throws ParseException {
    SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    
    Date date = formatter.parse("01-Jan-2017 00:47:13");
    System.out.println(date);
  }
}

按预期执行时打印 Sun Jan 01 00:47:13 CET 2017

但是,当我用日期中的 . 点替换每个 - 时:

package com.mycompany.mavenproject2;

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

public class Main {
  public static void main(String[] args) throws ParseException {
    SimpleDateFormat formatter = new SimpleDateFormat("dd.MMM.yyyy HH:mm:ss");
    
    Date date = formatter.parse("01.Jan.2017 00:47:13");
    System.out.println(date);
  }
}

代码在执行时抛出异常:

Exception in thread "main" java.text.ParseException: Unparseable date: "01.Jan.2017 00:47:13"
    at java.base/java.text.DateFormat.parse(DateFormat.java:395)
    at com.mycompany.mavenproject2.Main.main(Main.java:11)

我正在使用 Java 版本 12 和德语语言环境。

有人有想法吗?

java.time

我非常同意为您的日期和时间工作推荐 java.time 现代 Java 日期和时间 API 的评论。

使用这个格式化程序:

private static final DateTimeFormatter FORMATTER
        = DateTimeFormatter.ofPattern("dd.MMMuuuu HH:mm:ss", Locale.GERMAN);

做:

    LocalDateTime dateTime = LocalDateTime.parse("01.Jan.2017 00:47:13", FORMATTER);
    System.out.println(dateTime);

运行 在 Java 11 上使用德语语言环境时的输出:

2017-01-01T00:47:13

您可能想知道为什么与您的格式模式字符串相比我遗漏了第二个点?在 Java 11 上(也可能接近 Java 版本,可能从 Java 9 到 16)德语月份缩写带有一个点表示缩写,因此 Jan. 表示一月( January)等。所以在我的格式中 MMM 匹配 Jan. 然后 uuuu 匹配 2017.

更完整的故事 Java 从最多四个来源获取其区域设置数据,包括在不同区域设置中使用的月份缩写,但并非所有来源都同意德语月份缩写的样子。由于 Java 9 默认值为 CLDR,COMPAT,这意味着来自 CLDR(Unicode 通用语言环境数据存储库)的语言环境数据是首选。这些包括我提到的点。您可以通过将系统 属性 java.locale-providers 设置为不以 CLDR.

开头的值来获得不同的结果

你的代码出了什么问题?

我已经给了你一个提示:在某些 Java 版本中,德语月份缩写带有一个点。因此,在您使用点作为分隔符的示例中,您的 SimpleDateFormat 匹配 dd.MMM(没有第二个点)到 01.Jan.(有第二个点)。根据格式,现在应该出现一个点,但由于该点已经被消耗,SimpleDateFOrmat 查看了 2017,确定它不是一个点并抛出了您看到的异常。

真正令人惊讶的行为是在您的第一个示例中,其中 SimpleDateFormat 能够在没有任何点的情况下解析 01-Jan-2017 00:47:13,即使它认为月份缩写应该以点结尾。我以前确实见过数百个 SimpleDateFormat 令人惊讶的行为示例,但从未见过与此类似的示例。

所有这些惊喜让我说:请务必避免使用 SimpleDateFormat

如果你怀疑,我不怪你。所以要演示:

    SimpleDateFormat formatter = new SimpleDateFormat("MMMyyyy", Locale.GERMAN);
    
    System.out.println(formatter.format(0L));
    
    System.out.println(formatter.parse("Jan2017"));
    System.out.println(formatter.parse("Jan.2017"));

输出,还在Java11:

Jan.1970
Sun Jan 01 00:00:00 CET 2017
Sun Jan 01 00:00:00 CET 2017

我们看到 SimpleDateFormat 用点来格式化月份,并且能够解析带点和不带点的字符串。

这还不是全部。

    SimpleDateFormat formatter = new SimpleDateFormat("MMM", Locale.GERMAN);

    System.out.println(formatter.format(0L));

输出:

Jan

这次月份缩写的格式没有点。我不知道发生了什么。我再说一遍,忘掉令人困惑的 SimpleDateFormat class。真是臭名昭著的捣蛋鬼。

链接

  • Oracle tutorial: Date Time 解释如何使用 java.time。
  • ,关于来自不同来源的德语语言环境数据的问题。
  • How to parse month full form string using DateFormat in Java?,关于忘记指定语言环境进行解析的问题。