java.time.format.DateTimeParseException non può essere analizzato durante la migrazione da Java 8 a Java 11

Sep 02 2020

Funziona in Java 8

LocalDateTime ldt = LocalDateTime.parse("4/11/17 00:00 AM", DateTimeFormatter.ofPattern("d/M/yy hh:mm[ ][a]"));

Ma in Java 11 ottengo

java.time.format.DateTimeParseException: Text '4/11/17 00:00 AM' could not be parsed, unparsed text found at index 14
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2049)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)

Qualcuno può aiutarmi a capire perché questo è? Ho controllato i documenti e non vedo alcun cambiamento evidente alla classe DateTimeFormatter.

https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html

Risposte

9 rzwitserloot Sep 02 2020 at 18:40

Niente è cambiato. Stai commettendo un errore di stile del codice "piattaforma predefinita". Questo è un anti-pattern in cui usi un metodo rotto e rompi il tuo codice, ma il bug è quasi impossibile da trovare con gli unit test; il tuo codice finirà per esplodere al momento della produzione quando sono in gioco molti soldi e reputazione. "Fortunatamente", oggi hai trovato questo bug cambiando JDK e avendo la fortuna che le configurazioni locali siano in qualche modo differenti tra le tue installazioni JDK.

Ti suggerisco di provare antipatia per questi metodi sbagliati quanto me :) Questi metodi presumono che alcuni parametri siano 'qualunque cosa la tua piattaforma abbia come impostazione predefinita per questo valore', e i 3 colpevoli comuni sono, in ordine:

  • Codifica set di caratteri
  • Locale
  • Fuso orario

ofPattern(String)è uno di questi metodi; presume "la localizzazione predefinita della piattaforma", ed evidentemente, sulla tua installazione JDK8, è inglese o qualcosa di simile e sulla tua installazione JDK11 non lo è. L'analisi AMcome valore per un acampo dipende dalla locale; ovviamente, AM è un inglese, non avrebbe alcun senso in olandese o francese!

Ci sono 2 cose da fare per risolvere questo problema:

  1. Se il tuo IDE ha funzionalità per contrassegnare un metodo come 'non dovresti mai chiamarlo', dovresti considerare fortemente l'aggiunta di tutti questi metodi all'elenco, e se poi vuoi la piattaforma predefinita, che usi esplicitamente cose come Charset.defaultCharset()invece, per rendere chiaro che lo vuoi davvero.

  2. Correggi il tuo codice inserendo un , Locale.ENGLISHdopo il tuo schema e tutto tornerà a posto come la pioggia.

Un esempio:

LocalDateTime ldt = LocalDateTime.parse("4/11/17 00:00 AM",
  DateTimeFormatter.ofPattern("d/M/yy hh:mm[ ][a]",
  Locale.forLanguageTag("NL")));

fallirà su JDK8 tanto quanto su JDK11.

LocalDateTime ldt = LocalDateTime.parse("4/11/17 00:00 AM",
  DateTimeFormatter.ofPattern("d/M/yy hh:mm[ ][a]",
  Locale.ENGLISH));

funziona anche su JDK8 e JDK11.

NB: Un mucchio di localizzazioni non inglesi come .GERMANYe .ITALYe .FRANCEeffettivamente analizzano in qualche modo AM correttamente, ma come mostrato sopra, olandese (Paesi Bassi) è un esempio in cui non funzionerà.