java - 带有德语区域设置的 SimpleDateFormat - Java 8 与 Java 10+

标签 java java-8 simpledateformat java-9 java-10

我在一个legacy应用程序中有代码和一个测试用例,可以总结如下:

@Test
public void testParseDate() throws ParseException {
    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";

    DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
    Date date = dateFormatter.parse(toParse);

    //skipped assumptions
}

此测试在 Java 8 及更低版本中通过。但是,随着 Java 10 向上,这会导致 java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014"

记录在案: 除了 de_DE 之外,还会为语言环境抛出异常 de_CHde_ATde_LU

我知道日期格式是 changed with JDK 9 (JEP 252)。但是,我认为这是破坏向后兼容性的破坏性更改。摘录:

In JDK 9, the Unicode Consortium's Common Locale Data Repository (CLDR) data is enabled as the default locale data, so that you can use standard locale data without any further action.

In JDK 8, although CLDR locale data is bundled with the JRE, it isn’t enabled by default.

Code that uses locale-sensitive services such as date, time, and number formatting may produce different results with the CLDR locale data.

为星期几(Mo.)添加一个 . 可以弥补这一点,测试就会通过。但是,对于旧数据(以 XML 等序列化形式),这并不是真正的解决方案。

检查此stackoverflow post ,似乎该行为是针对德语区域设置的,可以通过使用 COMPAT 模式指定 java.locale.providers 来缓解。但是,我不喜欢依赖某些系统属性值的想法,原因可能有两个:

  1. JDK 下一版本中的更改。
  2. 在不同的环境中被遗忘。

我的问题是:

  • 如何在不重写/修改现有序列化数据或添加/更改系统属性(如 java.locale.providers)的情况下保持旧代码与此特定日期模式的向后兼容性,这可能会在不同的环境中被遗忘(应用服务器、独立 jar ......)?

最佳答案

我并不是说这是一个很好的解决方案,但它似乎是一种解决方法。

    Map<Long, String> dayOfWeekTexts = Map.of(1L, "Mo", 2L, "Di", 
            3L, "Mi", 4L, "Do", 5L, "Fr", 6L, "Sa", 7L, "So");
    Map<Long, String> monthTexts = Map.ofEntries(Map.entry(1L, "Jan"), 
            Map.entry(2L, "Feb"), Map.entry(3L, "Mär"), Map.entry(4L, "Apr"),
            Map.entry(5L, "Mai"), Map.entry(6L, "Jun"), Map.entry(7L, "Jul"),
            Map.entry(8L, "Aug"), Map.entry(9L, "Sep"), Map.entry(10L, "Okt"),
            Map.entry(11L, "Nov"), Map.entry(12L, "Dez"));

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendText(ChronoField.DAY_OF_WEEK, dayOfWeekTexts)
            .appendLiteral(' ')
            .appendText(ChronoField.MONTH_OF_YEAR, monthTexts)
            .appendPattern(" dd HH:mm:ss z Z yyyy")
            .toFormatter(Locale.GERMANY);

    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    OffsetDateTime odt = OffsetDateTime.parse(toParse, formatter);
    System.out.println(odt);
    ZonedDateTime zdt = ZonedDateTime.parse(toParse, formatter);
    System.out.println(zdt);

在我的 Oracle JDK 10.0.1 上运行的输出:

2014-08-18T11:25:26+02:00
2014-08-18T11:25:26+02:00[Europe/Berlin]

再一次,可能不存在好的解决方案。

java.time,现代 Java 日期和时间 API,允许我们指定用于格式化和解析的字段的文本。因此,我在星期几和月份都利用它,指定用于旧 COMPAT 或 JRE 语言环境数据的不带点的缩写。我使用 Java 9 Map.ofMap.ofEntries 来构建我们需要的 map 。如果这也适用于 Java 8,您必须找到其他方法来填充这两个映射,我相信您会这样做。

如果您确实需要老式的 java.util.Date(可能在遗留代码库中),请像这样转换:

    Date date = Date.from(odt.toInstant());
    System.out.println("As legacy Date: " + date);

在我的时区输出(欧洲/哥本哈根,可能与你的大致一致):

As legacy Date: Mon Aug 18 11:25:26 CEST 2014

策略建议

我在想,如果是我,我会考虑这样做:

  1. 等待。在 Java 中设置相关的系统属性:System.setProperty("java.locale.providers", "COMPAT,CLDR"); 这样它就不会在任何环境中被遗忘。 COMPAT 语言环境数据从 1.0 开始就已经存在(我相信,至少很接近),所以很多代码都依赖于它(不仅仅是你的)。在 Java 9 中,名称从 JRE 更改为 COMPAT。对我来说,这听起来像是将数据保留很长一段时间的计划。根据the early access documentation它仍将在 Java 11(下一个“长期支持”Java 版本)中可用,并且没有弃用警告等。如果在将来的某个 Java 版本中将其删除,您可能很快就会发现您可以在升级之前处理该问题。
  2. 使用我上面的解决方案
  3. 使用the locale service provider interface 与 Basil Bourque 相关联。毫无疑问,这是一个很好的解决方案,以防 COMPAT 数据在未来某个未知时间被删除。您甚至可以将 COMPAT 区域设置数据复制到您自己的文件中,这样他们就无法将它们从您手中夺走,在您这样做之前只需检查是否存在版权问题。我最后提到这个好的解决方案的原因是你说你不满意必须在你的程序可能运行的每个可能的环境中设置系统属性。据我所知,通过区域设置服务提供者接口(interface)使用您自己的区域设置数据仍然需要您设置相同的系统属性(仅设置为不同的值)。

关于java - 带有德语区域设置的 SimpleDateFormat - Java 8 与 Java 10+,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50411064/

相关文章:

java - 返回类的引用变量

java - 如何根据未知列数创建动态sql字符串

java - 使用 removeif 过滤包含对象的列表

java - 使用截断的时区信息解析 Java 日期

java - 将字符串转换为日期的年份格式不正确

java - 获取字符串值,当我从具有日期类型的 excel 中读取时 (apache poi)

java - 它不会抛出异常 ConcurrentModificationException

java - 有没有办法在java中连接位于同一台机器上的两个程序?

java - 在过滤器 findAny 之前, map 是否应用于所有列表?

java - 如何在 Java 8 中使用 lambda 表达式覆盖基类方法?