我已經測試了Java 1.8.0_121,並且有些區域確實缺失。
最明顯的方法來解決它是更新Java的版本 - 在Java中1.8.0_131上面的所有區域都可以 - 除了3個字母的名字(EST
,HST
等),低於更多。
但我知道生產環境中的更新並不像我們想的那樣簡單(也不快)。在這種情況下,您可以使用TZUpdater tool,它可以在不更改Java版本的情況下更新JDK的時區數據。
唯一的細節是,ZoneId
不與3字母縮寫(EST
,HST
等)工作。那是因爲這些名字是ambiguous and not standard。
但是,如果您想使用它們,您可以使用自定義ID的地圖。 ZoneId
配有內置地圖:
ZoneId.of("EST", ZoneId.SHORT_IDS);
的問題是,choices used in the SHORT_IDS
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
常數。
如果您不能更新您的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文件的格式完全相同)。
無論如何,解釋瞭如何閱讀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
的實現 - 它應該使用提供者的版本作爲關鍵字,而不是我正在做的區域ID等)。
當您更新Java版本時,必須立即丟棄此提供程序(因爲您不能讓2個提供程序創建具有相同標識的區域:如果新提供程序創建了一個已存在的標識,則會拋出一個當你嘗試註冊時是例外)。
您準確使用哪個版本的Java; Java 8的最新更新?時區信息經常使用新的Java更新進行更新,因此您可能正在使用特定的Java版本,其中您所引用的版本未定義。 – Jesper
我嘗試了Java 8更新144(目前是最新版本),它按預期工作。 – Jesper
Java版本「1.8.0_121」 Java™SE運行時環境(build 1.8.0_121-b13) Java HotSpot™64位服務器虛擬機(版本25.121-b13,混合模式) – dvelopp