SimpleDateFormat avec les parameters régionaux allemands – Java 8 vs Java 10

J’ai un code et un test-case dans une application héritée , qui peuvent être résumés comme suit:

@Test public void testParseDate() throws ParseException { Ssortingng toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014"; Ssortingng pattern = "EEE MMM dd HH:mm:ss z Z yyyy"; DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY); Date date = dateFormatter.parse(toParse); //skipped assumptions } 

Ce test passe en Java 8 et ci-dessous. Cependant, avec Java 9 ou Java 10, cela conduit à une java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014" .

Pour l’enregistrement : Outre de_DE , l’exception est également lancée pour les locales de_CH , de_AT , de_LU .

Je suis conscient du fait que le formatage de la date a été modifié avec JDK 9 ( JEP 252 ). Cependant, je considère que cette modification perturbe la rétrocompatibilité. Extrait:

Dans JDK 9, les données CLDR (Common Locale Data Repository) du consortium Unicode sont activées en tant que données de parameters régionaux par défaut, de sorte que vous pouvez utiliser des données de parameters régionaux standard sans aucune autre action.

Dans JDK 8, bien que les données de parameters régionaux CLDR soient regroupées avec le JRE, elles ne sont pas activées par défaut.

Le code qui utilise des services sensibles aux parameters régionaux, tels que la date, l’heure et le formatage des nombres, peut produire des résultats différents avec les données de parameters régionaux CLDR.

Ajouter un . pour le jour de la semaine ( Mo. ) compense pour cela et le test passerait. Cependant, ce n’est pas une solution réelle pour les anciennes données (sous forme sérialisée telle que XML).

En vérifiant ce post stackoverflow , il semble que le comportement est intentionnel pour les parameters régionaux allemands et peut être atténué en spécifiant java.locale.providers avec le mode COMPAT . Cependant, je n’aime pas l’idée de s’appuyer sur une valeur de propriété système pour deux raisons:

  1. changer dans les prochaines versions du JDK.
  2. être oublié dans différents environnements.

Ma question est:

  • Comment maintenir la compatibilité ascendante du code hérité avec ce modèle de date particulier, sans java.locale.providers / modifier les données sérialisées existantes ou append / modifier les propriétés du système (comme java.locale.providers ), qui peuvent être oubliées dans différents environnements (serveurs d’applications, bocaux autonomes, …)?

Je ne dis pas que c’est une bonne solution, mais cela semble être une solution.

  Map dayOfWeekTexts = Map.of(1L, "Mo", 2L, "Di", 3L, "Mi", 4L, "Do", 5L, "Fr", 6L, "Sa", 7L, "So"); Map monthTexts = Map.ofEnsortinges(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); Ssortingng 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); 

Sortie exécutée sur mon Oracle JDK 10.0.1:

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

Là encore, aucune solution intéressante ne peut exister.

java.time , l’API de date et heure Java moderne, nous permet de spécifier des textes à utiliser pour les champs à la fois pour le formatage et l’parsing. Je l’exploite donc à la fois pour le jour de la semaine et pour le mois, en spécifiant les abréviations sans point qui ont été utilisées avec les anciennes données locales COMPAT ou JRE. J’ai utilisé Java 9 Map.of et Map.ofEnsortinges pour créer les cartes dont nous avons besoin. Si cela doit fonctionner aussi avec Java 8, vous devez trouver un autre moyen de remplir les deux cartes, je vous fais confiance.

Si vous avez besoin d’un ancien fichier java.util.Date (probablement dans une base de code héritée), convertissez comme ceci:

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

La sortie dans mon fuseau horaire (Europe / Copenhague, correspond probablement à peu près à la vôtre):

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

Suggestion pour une stratégie

Je pense que si c’était moi, je penserais à procéder ainsi:

  1. Attendez . Définissez la propriété système appropriée depuis Java: System.setProperty("java.locale.providers", "COMPAT,CLDR"); ainsi, il ne sera oublié dans aucun environnement. Les données locales de COMPAT existent depuis la 1.0 (je crois, au moins à la fin), donc beaucoup de code en dépend (pas seulement le vôtre). Le nom a été changé de JRE à COMPAT en Java 9. Pour moi, cela peut sembler être un plan pour garder les données pendant un bon bout de temps. Selon la documentation sur les premiers access, il sera toujours disponible dans Java 11 (la prochaine version de Java «support à long terme») et sans avertissement de déchéance ou autre. Et si elle devait être supprimée dans une version future de Java, vous pourrez probablement trouver rapidement le problème avant de procéder à la mise à niveau.
  2. Utilisez ma solution ci-dessus .
  3. Utilisez l’interface du fournisseur de services régionaux associée à Basil Bourque. Il ne fait aucun doute que c’est la bonne solution au cas où les données COMPAT devraient être supprimées à une date inconnue. Vous pouvez même être en mesure de copier les données de l’environnement local COMPAT dans vos propres fichiers afin qu’ils ne puissent pas vous les enlever, vérifiez seulement s’il existe des problèmes de copyright avant de le faire. La raison pour laquelle je mentionne la bonne solution est que vous avez dit que vous n’êtes pas content d’avoir à définir une propriété système dans tous les environnements où votre programme peut être exécuté. Autant que je sache, l’utilisation de vos propres données de parameters régionaux via l’interface du fournisseur de services de parameters régionaux nécessitera toujours que vous définissiez la même propriété système (uniquement pour une valeur différente).

La valeur formatée en Java 8 était Fr Juni 15 00:20:21 MESZ +0900 2018 Mais elle a changé en Fr. Juin 15 00:20:21 MESZ +0900 2018 EEE comprend. CECI EST PROBLEME DE COMPATIBILITE et peu importe que les anciennes versions de code ne fonctionnent pas dans les nouvelles versions. (Désolé pour le traducteur) Si la chaîne de date est à vous, vous devez append un point pour les nouveaux utilisateurs. Ou faites en sorte que les utilisateurs utilisent Java 8 pour utiliser votre logiciel.

Cela peut ralentir le logiciel, l’utilisation de la méthode de la sous-chaîne est également bonne.

  Ssortingng toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014"; Ssortingng str = toParse.subssortingng(0, 2) + "." + toParse.subssortingng(2); Ssortingng pattern = "EEE MMM dd HH:mm:ss z Z yyyy"; DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY); System.out.println(dateFormatter.format(System.currentTimeMillis())); Date date = dateFormatter.parse(str); 

Désolé pour mon mauvais anglais.

Juste pour mentionner: SimpleDateFormat est une ancienne façon de formater les dates dont BTW n’est pas thread-safe. Depuis Java 8, il existe de nouveaux packages appelés java.time et java.time.format , que vous devez utiliser pour java.time.format dates. Pour vos besoins, vous devez utiliser la classe DateTimeFormatter .