具有德语语言环境的SimpleDateFormat - Java 8与Java 10+

rzo*_*rzo 49 java simpledateformat java-8 java-9 java-10

我在遗留应用程序中有代码和测试用例,可归纳如下:

@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
}
Run Code Online (Sandbox Code Playgroud)

该测试通过Java 8及以下版本.然而,随着Java 10向上,这导致了一个java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014".

备案:另外de_DE,异常也抛出了语言环境 de_CH,de_AT,de_LU.

我知道使用JDK 9(JEP 252)更改了日期格式.但是,我认为这是破坏向后兼容性的颠覆性变化.摘录:

在JDK 9中,Unicode Consortium的公共区域设置数据存储库(CLDR)数据作为默认区域设置数据启用,因此您可以使用标准区域设置数据而无需任何进一步操作.

在JDK 8中,虽然CLDR区域设置数据与JRE捆绑在一起,但默认情况下不启用它.

使用区域设置敏感服务(如日期,时间和数字格式)的代码可能会使用CLDR区域设置数据生成不同的结果.

添加.一周的某一天(Mo.)可以补偿这一点并且测试将通过.但是,这不是旧数据的真正解决方案(以序列化形式,如XML).

检查这个计算器后,似乎行为是故意的德语区域,并且可以通过指定减轻java.locale.providersCOMPAT模式.但是,我不喜欢依赖于某些系统属性值的想法有两个原因:

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

我的问题是:

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

Ole*_*.V. 19

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

    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);
Run Code Online (Sandbox Code Playgroud)

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

2014-08-18T11:25:26+02:00
2014-08-18T11:25:26+02:00[Europe/Berlin]
Run Code Online (Sandbox Code Playgroud)

然后,再也没有好的解决方案.

java.time,现代Java日期和时间API,允许我们指定用于格式化和解析的字段的文本.因此,我将其用于星期和月份,指定与旧COMPAT或JRE语言环境数据一起使用的没有点的缩写.我使用了Java 9 Map.ofMap.ofEntries构建了我们需要的地图.如果这也适用于Java 8,你必须找到一些其他方法来填充这两个地图,我相信你这样做.

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

    Date date = Date.from(odt.toInstant());
    System.out.println("As legacy Date: " + date);
Run Code Online (Sandbox Code Playgroud)

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

As legacy Date: Mon Aug 18 11:25:26 CEST 2014
Run Code Online (Sandbox Code Playgroud)

对策略的建议

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

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

  • "Mar"会写成"Mär"(有时甚至是"Mrz"). (3认同)