Java没有关于所有IANA时区的信息 [英] Java doesn't have information about all IANA time zones

查看:237
本文介绍了Java没有关于所有IANA时区的信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将来自前端的值映射到 ZoneId 类,如下所示:

I'm trying to map the values that come from the Front-End to ZoneId class like this:

Optional.ofNullable(timeZone).map(ZoneId::of).orElse(null)

对于大多数时区,它工作正常,但是,对于某些值,Java抛出异常:

For most time zones it works fine, however, for some values Java throws exception:

java.time.zone.ZoneRulesException: Unknown time-zone ID: America/Punta_Arenas

但是,它是根据IANA的有效时区:
https://www.iana.org/time -zones

However, it is a valid time-zone according to IANA: https://www.iana.org/time-zones


Zone America / Punta_Arenas -4:43:40 - LMT 1890

Zone America/Punta_Arenas -4:43:40 - LMT 1890

我在考虑为这些时区使用偏移量(只是硬编码值),但我想应该有更方便的方法来解决这个问题。有没有一种方法可以处理Java?

I was thinking about using offset for such time-zones (just to hardcode values), but I guess there should be more convenient way to solve the issue. Is there a way Java can handle that?

不支持的其他时区:


  • America / Punta_Arenas

  • Asia / Atyrau

  • Asia / Famagusta

  • Asia / Yangon

  • EST

  • 欧洲/萨拉托夫

  • HST

  • MST

  • ROC

  • America/Punta_Arenas
  • Asia/Atyrau
  • Asia/Famagusta
  • Asia/Yangon
  • EST
  • Europe/Saratov
  • HST
  • MST
  • ROC

我的Java版本:1.8.0_121Java(TM)SE运行时环境(版本1.8.0_121-b13)Java HotSpot (TM)64位服务器VM(版本25.121-b13,混合模式)

My Java version: "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

推荐答案

我已经使用Java 1.8进行了测试。 0_121和一些区域确实缺失。

I've tested with Java 1.8.0_121 and some zones are really missing.

最明显的解决方法是更新Java的版本 - 在Java 1.8.0_131中,所有上述区域都可用 - 除了3个字母的名字( EST HST 等),详情如下。

The most obvious way to fix it is to update Java's version - in Java 1.8.0_131 all the zones above are available - except the 3-letter names (EST, HST, etc), more on that below.

但我知道生产环境中的更新并不像我们想的那么容易(也不快)。在这种情况下,您可以使用 TZUpdater工具,它可以在不改变Java版本的情况下更新JDK的时区数据。

But I know that updates in production environments are not as easy (nor fast) as we'd like. In this case, you could use the TZUpdater tool, which can update JDK's timezone data without changing Java's version.

唯一的细节是 ZoneId 不适用于3个字母的缩写( EST HST 等等) 。那是因为这些名称是模棱两可而不是标准

The only detail is that ZoneId doesn't work with the 3-letter abbreviations (EST, HST and so on). That's because those names are ambiguous and not standard.

但是,如果你想使用它们,你可以使用自定义ID的地图。 ZoneId 附带内置地图:

If you want to use them, though, you can use a map of custom ID's. ZoneId comes with a built-in map:

ZoneId.of("EST", ZoneId.SHORT_IDS);

问题在于 SHORT_IDS 地图中使用的选项 - 就像任何其他选择 - 任意甚至有争议。如果你想为每个缩写使用不同的区域,只需创建你自己的地图:

The problem is that the choices used in the SHORT_IDS map are - like any other choice - arbitrary and even controversial. If you want to use different zones for each abbreviation, just create your own map:

Map<String, String> map = new HashMap<>();
map.put("EST", "America/New_York");
... put how many names you want
System.out.println(ZoneId.of("EST", map)); // creates America/New_York

3个字母名称的唯一例外当然是 GMT UTC ,但在这种情况下,最好只使用 ZoneOffset.UTC 常量。

The only exceptions for 3-letter names are, of course, GMT and UTC, but in this case it's better to just use the ZoneOffset.UTC constant.

如果您无法更新Java版本或运行TZUpdater工具,那么还有另一种(更难的)替代方案。

If you can't update your Java version nor run the TZUpdater tool, there's another (much more difficult) alternative.

您可以扩展 java.time.zone.ZoneRulesProvider 类,并创建一个可以创建缺失ID的提供程序。类似的东西:

You can extend the java.time.zone.ZoneRulesProvider class and make a provider that can create the missing ID's. Something like that:

public class MissingZonesProvider extends ZoneRulesProvider {

    private Set<String> missingIds = new HashSet<>();

    public MissingZonesProvider() {
        missingIds.add("America/Punta_Arenas");
        missingIds.add("Europe/Saratov");
        // add all others
    }

    @Override
    protected Set<String> provideZoneIds() {
        return this.missingIds;
    }

    @Override
    protected ZoneRules provideRules(String zoneId, boolean forCaching) {
        ZoneRules rules = null;
        if ("America/Punta_Arenas".equals(zoneId)) {
            rules = // create rules for America/Punta_Arenas
        }
        if ("Europe/Saratov".equals(zoneId)) {
            rules = // create rules for Europe/Saratov
        }
        // and so on

        return rules;
    }

    // returns a map with the ZoneRules, check javadoc for more details
    @Override
    protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
        TreeMap<String, ZoneRules> map = new TreeMap<>();
        ZoneRules rules = provideRules(zoneId, false);
        if (rules != null) {
            map.put(zoneId, rules);
        }
        return map;
    }
}

创建 ZoneRules 是最复杂的部分。

一种方法是获取最新的 IANA文件并阅读它们。您可以查看 JDK源代码,看看它是如何创建 ZoneRules 的(虽然我是不确定JDK中的文件是否与IANA的文件格式完全相同。)

One way is to get the latest IANA files and read them. You can take a look at JDK source code to see how it creates ZoneRules from that (although I'm not sure if the file that's inside JDK is in the exact same format as IANA's files).

无论如何,此链接说明了如何阅读IANA的文件。然后你可以看看 ZoneRules javadoc ,了解如何将IANA信息映射到Java类。在这个答案中,我创建了一个非常简单的 ZoneRules 规则,所以你可以基本了解如何做。

Anyway, this link explains how to read IANA's files. Then you can take a look at ZoneRules javadoc to know how to map IANA information to Java classes. In this answer I create a very simple ZoneRules with just 2 transition rules, so you can get a basic idea of how to do it.

然后你需要注册提供者:

Then you need to register the provider:

ZoneRulesProvider.registerProvider(new MissingZonesProvider());

现在新区域将可用:

ZoneId.of("America/Punta_Arenas");
ZoneId.of("Europe/Saratov");
... and any other you added in the MissingZonesProvider class

还有其他方法可以使用提供者(而不是注册),检查javadoc 了解更多详情。在同一个javadoc中还有关于如何正确实现区域规则提供程序的更多细节(我的上面的版本非常简单,可能它缺少一些细节,比如 provideVersions 的实现 - 它应该使用提供者的版本作为密钥,而不是我正在做的区域ID等。)

There are other ways to use the provider (instead of registering), check the javadoc for more details. In the same javadoc there are also more details about how to properly implement a zone rules provider (my version above is very simple and probably it's missing some details, like the implementation of provideVersions - it should use the provider's version as a key, not the zone ID as I'm doing, etc).

当然,这个提供者必须立即丢弃更新Java版本(因为您不能有2个提供程序创建具有相同ID的区域:如果新提供程序创建已存在的ID,则在您尝试注册时会引发异常。)

Of course this provider must be discarded as soon as you update the Java version (because you can't have 2 providers that create zones with the same ID: if the new provider creates an ID that already exists, it throws an exception when you try to register it).

这篇关于Java没有关于所有IANA时区的信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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