问题描述
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.
表示 Januar(一月)等。所以在我的格式中 {{1 }} 匹配 MMM
,然后 Jan.
匹配 2017。
对于更完整的故事,Java 从多达四个来源获取其语言环境数据,包括在不同语言环境中使用的月份缩写,并非所有来源都同意德语月份缩写的样子。由于 Java 9 的默认值是 uuuu
,这意味着首选来自 CLDR(Unicode 通用语言环境数据存储库)的语言环境数据。这些包括我提到的点。您可以通过将系统属性 CLDR,COMPAT
设置为不以 java.locale-providers
开头的值来获得不同的结果。
您的代码出了什么问题?
我已经给你提示了:在某些 Java 版本中,德语月份的缩写带有点。因此,在您使用点作为分隔符的示例中,您的 CLDR
将 SimpleDateFormat
(没有第二个点)匹配到 dd.MMM
(有第二个点)。根据格式,现在应该出现一个点,但由于该点已被消耗,01.Jan.
看着 SimpleDateFOrmat
,认为它不是一个点并抛出您看到的异常。
真正令人惊讶的行为是在您的第一个示例中,其中 2017
能够解析没有任何点的 SimpleDateFormat
,即使它认为月份缩写应该以点结尾。我以前见过数百个 01-Jan-2017 00:47:13
令人惊讶的行为示例,但从未见过与此类似的示例。
所有这些惊喜让我说:一定要避免使用 SimpleDateFormat
。
如果你持怀疑态度,我不怪你。所以为了演示:
SimpleDateFormat
输出,仍在 Java 11 上:
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"));
我们看到 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
类。这是一个臭名昭著的麻烦制造者。
链接
- Oracle tutorial: Date Time 解释如何使用 java.time。
- JDK dateformatter parsing DayOfWeek in German locale,java8 vs java9,关于来自不同来源的德语区域设置数据的问题。
- How to parse month full form string using DateFormat in Java?,一个关于忘记指定用于解析的语言环境的问题。