Fecha y hora de Java 8: analizar la cadena ISO 8601 sin dos puntos en el desplazamiento
datetime iso8601 (4)
Esta pregunta ya tiene una respuesta aquí:
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.