java datetime iso8601 timezone-offset datetime-parsing

Fecha y hora de Java 8: analizar la cadena ISO 8601 sin dos puntos en el desplazamiento



datetime iso8601 (4)

Intentamos analizar la siguiente cadena de fecha y hora ISO 8601 con desplazamiento de zona horaria:

final String input = "2022-03-17T23:00:00.000+0000"; OffsetDateTime.parse(input); LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME);

Ambos enfoques fallan (lo cual tiene sentido ya que OffsetDateTime también usa DateTimeFormatter.ISO_OFFSET_DATE_TIME ) debido a los dos puntos en el desplazamiento de la zona horaria.

java.time.format.DateTimeParseException: el texto ''2022-03-17T23: 00: 00.000 + 0000'' no se pudo analizar en el índice 23

Pero según Wikipedia hay 4 formatos válidos para un desplazamiento de zona horaria:

<time>Z <time>±hh:mm <time>±hhmm <time>±hh

Otros marcos / idiomas pueden analizar esta cadena sin ningún problema, por ejemplo, la Date() Javascript Date() o Jacksons ISO8601Utils (discuten este problema here )

Ahora podríamos escribir nuestro propio DateTimeFormatter con un RegEx complejo, pero en mi opinión, la biblioteca java.time debería poder analizar esta cadena ISO 8601 válida de forma predeterminada, ya que es válida.

Por ahora usamos Jacksons ISO8601DateFormat , pero preferiríamos usar la biblioteca oficial date.time para trabajar. ¿Cuál sería su enfoque para abordar este problema?


Como no tiene dos puntos, ¿puede usar su propia cadena de formato:

final String input = "2022-03-17T23:00:00.000+0000"; DateFormat df = new SimpleDateFormat("yyyy-MM-dd''T''HH:mm:ss.SSSZ"); Date parsed = df.parse(input); System.out.println(parsed);


No lo llamaría una solución sino una solución alternativa. La plantilla Z de SimpleDateFormat admite la sintaxis de zona horaria que mostró, por lo que puede hacer algo como esto:

final String input = "2022-03-17T23:00:00.000+0000"; try { OffsetDateTime.parse(input); LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME); } catch (DateTimeParseException e) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd''T''HH:mm:ss.SZ", Locale.GERMANY); sdf.parse(input); }

Todavía está utilizando las bibliotecas oficiales que se envían con la JVM. Uno no es parte de la biblioteca date.time, pero aún así ;-)


No necesita escribir una expresión regular compleja: puede crear un DateTimeFormatter que funcione con ese formato fácilmente:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd''T''HH:mm:ss.SSSX", Locale.ROOT); OffsetDateTime odt = OffsetDateTime.parse(input, formatter);

Eso también aceptará "Z" en lugar de "0000". No aceptará "+00: 00" (con los dos puntos o similar. Eso es sorprendente dada la documentación, pero si su valor siempre tiene el desplazamiento UTC sin los dos puntos, debería estar bien.


Si desea analizar todos los formatos válidos de compensaciones ( Z , ±hh:mm , ±hhmm y ±hh ), una alternativa es usar un java.time.format.DateTimeFormatterBuilder con patrones opcionales (desafortunadamente, parece que no hay un solo letra del patrón para que coincida con todos):

DateTimeFormatter formatter = new DateTimeFormatterBuilder() // date/time .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) // offset (hh:mm - "+00:00" when it''s zero) .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd() // offset (hhmm - "+0000" when it''s zero) .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd() // offset (hh - "Z" when it''s zero) .optionalStart().appendOffset("+HH", "Z").optionalEnd() // create formatter .toFormatter(); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+0000", formatter)); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00", formatter)); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00:00", formatter)); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000Z", formatter));

Los cuatro casos anteriores lo analizarán a 2022-03-17T23:00Z .

También puede definir un patrón de cadena único si lo desea, utilizando [] para delimitar las secciones opcionales:

// formatter with all possible offset patterns DateTimeFormatter formatter = DateTimeFormatter .ofPattern("yyyy-MM-dd''T''HH:mm:ss.SSS[xxx][xx][X]");

Este formateador también funciona para todos los casos, al igual que el formateador anterior anterior. Consulte el javadoc para obtener más detalles sobre cada patrón.

Notas:

  • Un formateador con secciones opcionales como las anteriores es bueno para analizar, pero no para formatear. Al formatear, imprimirá todas las secciones opcionales, lo que significa que imprimirá el desplazamiento muchas veces. Entonces, para formatear la fecha, simplemente use otro formateador.
  • El segundo formateador acepta exactamente 3 dígitos después del punto decimal (debido a .SSS ). Por otro lado, ISO_LOCAL_DATE_TIME es más flexible: los segundos y nanosegundos son opcionales, y también acepta de 0 a 9 dígitos después del punto decimal. Elija el que mejor funcione para sus datos de entrada.