2017-08-09 6 views
5

Je suis en train de cartographier les valeurs qui viennent du Front-End à ZoneId classe comme ceci:Java ne dispose pas d'informations sur tous les fuseaux horaires de l'IANA

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

Pour la plupart des fuseaux horaires, il fonctionne très bien, Cependant, pour certaines valeurs Java génère une exception:

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

Cependant, il est valide le fuseau horaire selon l'IANA: https://www.iana.org/time-zones

Zone Amérique/Punta_Arenas -4: 43: 40 - LMT 1890

Je pensais à en offset pour les fuseaux horaires (juste pour les valeurs hardcode), mais je suppose qu'il devrait être plus moyen pratique pour résoudre le problème. Existe-t-il un moyen pour Java de gérer cela?

Autres fuseaux horaires qui ne sont pas pris en charge:

  • Amérique/Punta_Arenas
  • Asie/Atyrau
  • Asie/Famagouste
  • Asie/Yangon
  • EST
  • Europe/Saratov
  • HST
  • MST
  • ROC

Ma version Java: "1.8.0_121" Java (TM) SE Runtime Environment (build 1.8.0_121-b13) Java HotSpot (TM) 64 bits serveur VM (build 25.121- b13, mode mixte)

+2

Quelle version de Java utilisez-vous exactement? la dernière mise à jour de Java 8? Les informations de fuseau horaire sont fréquemment mises à jour avec de nouvelles mises à jour Java, il est possible que vous utilisiez une version Java spécifique dans laquelle celle que vous référencez n'est pas définie. – Jesper

+0

Je l'ai essayé avec Java 8 mise à jour 144 (actuellement la dernière version) et cela fonctionne comme prévu. – Jesper

+0

version java "1.8.0_121" Environnement d'exécution Java (TM) SE (version 1.8.0_121-b13) Java serveur HotSpot (TM) 64 bits (version 25.121-b13, mode mixte) – dvelopp

Répondre

7

J'ai testé avec Java 1.8.0_121 et certaines zones sont vraiment manquantes. La manière la plus évidente de le réparer est de mettre à jour la version de Java - dans Java 1.8.0_131 toutes les zones ci-dessus sont disponibles - excepté les noms de 3 lettres (EST, HST, etc.), plus sur cela ci-dessous.

Mais je sais que les mises à jour dans les environnements de production ne sont pas aussi faciles (ni rapides) que nous le souhaiterions. Dans ce cas, vous pouvez utiliser le TZUpdater tool, qui peut mettre à jour les données de fuseau horaire de JDK sans modifier la version de Java.


Le seul détail est que ZoneId ne fonctionne pas avec les abréviations 3 lettres (EST, HST et ainsi de suite). C'est parce que ces noms sont ambiguous and not standard. Cependant, si vous souhaitez les utiliser, vous pouvez utiliser une carte d'ID personnalisée. ZoneId est livré avec une carte intégrée:

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

Le problème est que le choices used in the SHORT_IDS map sont - comme tout autre choix - arbitraire et même controversé.Si vous souhaitez utiliser différentes zones pour chaque abréviation, il suffit de créer votre propre carte:

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 

Les seules exceptions pour les noms de 3 lettres sont, bien sûr, GMT et UTC, mais dans ce cas, il est mieux vaut simplement utiliser la constante ZoneOffset.UTC.


Si vous ne pouvez pas mettre à jour votre version Java, ni exécuter l'outil tzupdater, il y a un autre (beaucoup plus difficile) alternative.

Vous pouvez étendre la classe java.time.zone.ZoneRulesProvider et faire un fournisseur qui peut créer les ID manquants de. Quelque chose comme ça:

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; 
    } 
} 

Créer la ZoneRules est la partie la plus compliquée.

Une façon est d'obtenir la dernière IANA files et les lire. Vous pouvez jeter un oeil à JDK source code pour voir comment il crée ZoneRules de ce (même si je ne sais pas si le fichier qui est à l'intérieur est dans JDK exactement le même format que les fichiers de l'IANA).

Quoi qu'il en soit, explique comment lire les fichiers de l'IANA. Ensuite, vous pouvez jeter un oeil à ZoneRules javadoc pour savoir comment mapper l'information de l'IANA aux classes Java. En this answer je crée un très simple ZoneRules avec seulement 2 règles de transition, de sorte que vous pouvez avoir une idée de base de la façon de le faire.

Ensuite, vous devez enregistrer le fournisseur:

ZoneRulesProvider.registerProvider(new MissingZonesProvider()); 

Et maintenant les nouvelles zones seront disponibles:

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

Il existe d'autres façons d'utiliser le fournisseur (au lieu de l'enregistrement), check the javadoc pour plus de détails. Dans le même javadoc il y a aussi plus de détails sur la manière d'implémenter un fournisseur de règles de zone (ma version est au-dessus très simple et probablement il manque quelques détails, comme la mise en œuvre de provideVersions - il doit utiliser la version du fournisseur comme une clé, pas identifiant de zone comme je le fais, etc).

Bien sûr, ce fournisseur doit être supprimé dès que vous mettez à jour la version Java (car vous ne pouvez pas avoir 2 fournisseurs qui créent des zones avec le même ID: si le nouveau fournisseur crée un ID existant, il lance un exception lorsque vous essayez de l'enregistrer).