java.time.format.DateTimeParseException não pôde ser analisado ao migrar de Java 8 para Java 11

Sep 02 2020

Isso funciona em Java 8

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

Mas no Java 11 eu consigo

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)

Alguém pode me ajudar a entender por que isso acontece? Verifiquei os documentos e não vejo nenhuma mudança óbvia na 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

Respostas

9 rzwitserloot Sep 02 2020 at 18:40

Nada mudou. Você está cometendo um erro de estilo de código de 'plataforma padrão'. Este é um antipadrão em que você usa um método quebrado e quebra seu código, mas o bug é quase impossível de encontrar com testes de unidade; seu código acabará explodindo no momento da produção, quando muito dinheiro e reputação estão em jogo. "Felizmente", você encontrou esse bug hoje trocando JDKs e tendo a sorte de as configurações de localidade serem de alguma forma diferentes entre as instalações JDK.

Eu sugiro que você cresça tanto quanto eu não gosto por esses métodos ruins :) Esses métodos são aqueles que presumem que algum parâmetro seja 'o que quer que sua plataforma tenha como padrão para este valor', e os 3 culpados comuns são, em ordem:

  • Codificação Charset
  • Localidade
  • Fuso horário

ofPattern(String)é um desses métodos; ele presume 'o local padrão da plataforma' e, evidentemente, na instalação do JDK8, é inglês ou algo semelhante e na instalação do JDK11 não é. A análise AMcomo valor para um acampo depende da localidade; obviamente, AM é um inglês, não faria sentido em holandês ou francês!

Existem 2 coisas a serem feitas para resolver este problema:

  1. Se o seu IDE tem recursos para marcar um método como 'você realmente nunca deve chamar isso', você deve considerar fortemente a adição de todos esses métodos à lista e, se quiser o padrão da plataforma, usar explicitamente coisas como Charset.defaultCharset(), claro que você realmente quer isso.

  2. Corrija seu código empurrando um , Locale.ENGLISHdepois de seu padrão e tudo ficará bem como chuva novamente.

Um exemplo:

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

irá falhar no JDK8 tanto quanto no JDK11.

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

funciona em JDK8 e JDK11 também.

NB: Um monte de localidades diferentes do inglês como .GERMANYe .ITALYe .FRANCErealmente analisam AM corretamente, mas como mostrado acima, holandês (Holanda) é um exemplo em que isso não funcionará.