带有德语语言环境的 SimpleDateFormat - Java 8 与 Java 10+ [英] SimpleDateFormat with German Locale - Java 8 vs Java 10+

查看:25
本文介绍了带有德语语言环境的 SimpleDateFormat - Java 8 与 Java 10+的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have code and a test-case in a legacy application, which can be summarized as follows:

@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".

This test passes in Java 8 and below. However with Java 10 upwards this leads to a java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014".

记录:除了de_DE,locales也会抛出异常de_CHde_ATde_LU.

For the record: Besides de_DE, the exception is also thrown for the locales de_CH, de_AT, de_LU.

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

I am aware of the fact, that Date formatting was changed with JDK 9 (JEP 252). However, I consider this to be a disruptive change breaking backwards compatibility. Excerpted:

在 JDK 9 中,Unicode Consortium 的 Common Locale Data Repository (CLDR) 数据被启用为默认语言环境数据,因此您无需任何进一步操作即可使用标准语言环境数据.

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.

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

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

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

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

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

Adding a . for the day of the week (Mo.) compensates for this and test would pass. However, this is not a real solution for old data (in serialized form such as XML).

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

Checking this stackoverflow post, it seems that the behaviour is intentional for the German locale and can be mitigated by specifying java.locale.providers with COMPAT mode. However, I do not like the idea to rely on some system property value for two reasons as it might:

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

我的问题是:

  • 如何在不重写/修改现有序列化数据或添加/更改系统属性(如 java.locale.providers)的情况下保持旧代码与此特定日期模式的向后兼容性,可能会在不同的环境(应用服务器、独立的 jars 等)中被遗忘?
  • How can I maintain backwards compatibility of legacy code with this particular date pattern, without re-writing / modifying existing serialized data or adding / changing system properties (like java.locale.providers), which may be forgotten in different environments (application servers, standalone jars, ...) ?

推荐答案

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

I don’t say it’s a nice solution, but it seems to be a way through.

    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 上运行的输出:

Output running on my Oracle JDK 10.0.1:

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

再说一次,可能没有好的解决方案.

Then again, no nice solution may exist.

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

java.time, the modern Java date and time API, allows us to specify texts to use for fields for both formatting and parsing. So I exploit that for both day of week and for month, specifying the abbreviations without dot that were used with the old COMPAT or JRE locale data. I have used the Java 9 Map.of and Map.ofEntries for building the maps we need. If this is to work in Java 8 too, you must find some other way to populate the two maps, I trust you to do that.

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

If you do need an old-fashioned java.util.Date (likely in a legacy code base), convert like this:

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

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

Output in my time zone (Europe/Copenhagen, probably roughly agrees with yours):

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

策略建议

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

Suggestion for a strategy

I am thinking that if that were me, I’d consider proceeding this way:

  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 区域设置数据复制到您自己的文件中,这样他们就无法将它们从您身边带走,只需在这样做之前检查是否存在版权问题.我最后提到这个不错的解决方案的原因是你说你不满意必须在你的程序可能运行的每个可能的环境中设置系统属性.据我所知,通过语言环境服务提供者接口使用您自己的语言环境数据仍然需要您设置相同的系统属性(仅设置为不同的值).
  1. Wait. Set the relevant system property from within Java: System.setProperty("java.locale.providers", "COMPAT,CLDR"); so it won’t be forgot in any environment. The COMPAT locale data have been around since 1.0 (I believe, at least close), so a lot of code out there depends on it (not only yours). The name was changed from JRE to COMPAT in Java 9. To me this may sound like a plan to keep the data around for quite a while still. According to the early access documentation it will still be available in Java 11 (the next "long term support" Java version) and with no deprecation warning or the like. And should it be removed in some future Java version, you will probably be able to find out soon enough that you can deal with the problem before upgrading.
  2. Use my solution above.
  3. Use the locale service provider interface that Basil Bourque linked to. There is no doubt that this is the nice solution in case the COMPAT data should be removed some unknown time in the future. You may even be able to copy the COMPAT locale data into your own files so they can’t take them away from you, only check if there are copyright issues before you do so. The reason why I mention the nice solution last is you said you aren’t happy with having to set a system property in every possible environment where your program may run. As far as I can tell, using your own locale data through the locale service provider interface will still require you to set the same system property (only to a different value).

这篇关于带有德语语言环境的 SimpleDateFormat - Java 8 与 Java 10+的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆