gpt4 book ai didi

Java 没有关于所有 IANA 时区的信息

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:58:01 25 4
gpt4 key购买 nike

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

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

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

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

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

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

我正在考虑为这样的时区使用偏移量(只是为了硬编码值),但我想应该有更方便的方法来解决这个问题。 Java 有办法处理吗?

不支持的其他时区:

  • 美国/蓬塔阿雷纳斯
  • 亚洲/阿特劳
  • 亚洲/法马古斯塔
  • 亚洲/仰光
  • 东部标准时间
  • 欧洲/萨拉托夫
  • 高铁
  • 山顶
  • 中华民国

我的 Java 版本:“1.8.0_121”Java(TM) SE 运行时环境(内部版本 1.8.0_121-b13)Java HotSpot(TM) 64 位服务器虚拟机(内部版本 25.121-b13,混合模式)

最佳答案

我已经用 Java 1.8.0_121 测试过,有些区域确实缺失了。

修复它的最明显方法是更新 Java 版本 - 在 Java 1.8.0_131 中,上述所有区域都可用 - 除了 3 个字母的名称(ESTHST 等),更多内容见下文。

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


唯一的细节是 ZoneId 不适用于 3 字母缩写(ESTHST 等)。那是因为这些名字是 ambiguous and not standard .

不过,如果您想使用它们,您可以使用自定义 ID 的映射。 ZoneId 带有内置 map :

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

问题是 choices used in the SHORT_IDS map就像任何其他选择一样,是武断的,甚至是有争议的。如果您想为每个缩写使用不同的区域,只需创建您自己的 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 字母名称的唯一异常(exception)当然是 GMTUTC,但在这种情况下最好只使用 ZoneOffset.UTC 常量。


如果您不能更新您的 Java 版本,也不能运行 TZUpdater 工具,还有另一种(更难)的替代方法。

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

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 files并阅读它们。你可以看看JDK source code查看它如何从中创建 ZoneRules(尽管我不确定 JDK 中的文件是否与 IANA 文件的格式完全相同)。

无论如何,this link解释了如何阅读 IANA 的文件。那你可以看看ZoneRules javadoc了解如何将 IANA 信息映射到 Java 类。在 this answer我创建了一个非常简单的 ZoneRules,只有 2 个转换规则,因此您可以基本了解如何操作。

然后你需要注册提供者:

ZoneRulesProvider.registerProvider(new MissingZonesProvider());

现在新区域将可用:

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

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

当然,一旦您更新了 Java 版本,就必须丢弃该提供者(因为您不能让 2 个提供者创建具有相同 ID 的区域:如果新提供者创建一个已经存在的 ID,它会抛出异常当您尝试注册它时)。

关于Java 没有关于所有 IANA 时区的信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45592878/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com