studio - simpledateformat java ejemplo
SimpleDateFormat con configuraciĆ³n regional alemana-Java 8 vs Java 10+ (3)
Tengo un código y un caso de prueba en una aplicación heredada , que se puede resumir de la siguiente manera:
@Test
public void testParseDate() throws ParseException {
String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";
DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
Date date = dateFormatter.parse(toParse);
//skipped assumptions
}
Esta prueba pasa en Java 8 y más abajo. Sin embargo, con Java 10 hacia arriba, esto conduce a una java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014"
.
Para el registro : además de de_DE
, también se lanza la excepción para los locales de_CH
, de_AT
, de_LU
.
Soy consciente del hecho de que el formato de fecha se cambió con JDK 9 ( JEP 252 ). Sin embargo, considero que esto es un cambio disruptivo que rompe la compatibilidad hacia atrás. Extraído:
En JDK 9, los datos del repositorio de datos de configuración regional común (CLDR) de Unicode Consortium se habilitan como los datos de configuración regional predeterminados, de modo que puede utilizar los datos de configuración regional estándar sin ninguna otra acción.
En JDK 8, aunque los datos de configuración regional de CLDR se incluyen con el JRE, no están habilitados de forma predeterminada.
El código que utiliza servicios sensibles al entorno local, como la fecha, la hora y el formato de los números, puede producir resultados diferentes con los datos del entorno local de CLDR.
Añadiendo un .
para el día de la semana ( Mo.
) compensa esto y la prueba pasaría. Sin embargo, esta no es una solución real para datos antiguos (en forma serializada como XML).
Al revisar esta publicación de stackoverflow , parece que el comportamiento es intencional para la configuración regional alemana y se puede mitigar especificando java.locale.providers
con el modo COMPAT
. Sin embargo, no me gusta la idea de confiar en algún valor de propiedad del sistema por dos razones:
- Cambio en los próximos lanzamientos del JDK.
- Ser olvidado en diferentes ambientes.
Mi pregunta es:
- ¿Cómo puedo mantener la compatibilidad retroactiva del código heredado con este patrón de fecha en particular, sin volver a escribir / modificar los datos serializados existentes o agregar / cambiar las propiedades del sistema (como
java.locale.providers
), que pueden olvidarse en diferentes entornos (servidores de aplicaciones, frascos independientes, ...)?
El valor formateado en java 8 fue Fr Juni 15 00:20:21 MESZ +0900 2018 Pero cambió a Fr. Juni 15 00:20:21 MESZ +0900 2018 EEE incluye. ESTE ES UN PROBLEMA DE COMPATIBILIDAD y no importa que las versiones anteriores del código no funcionen en las versiones más nuevas. (Lo siento por el traductor) Si la cadena de fecha es suya, debe agregar un punto para los usuarios de la nueva versión. O haga que los usuarios utilicen Java 8 para usar su software.
Puede hacer que el software sea más lento, el método de subcadena también es bueno.
String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
String str = toParse.substring(0, 2) + "." + toParse.substring(2);
String 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);
Lo siento de nuevo por mi mal inglés.
No digo que sea una buena solución, pero parece ser una forma de lograrlo.
Map<Long, String> dayOfWeekTexts = Map.of(1L, "Mo", 2L, "Di",
3L, "Mi", 4L, "Do", 5L, "Fr", 6L, "Sa", 7L, "So");
Map<Long, String> monthTexts = Map.ofEntries(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);
String 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);
Salida ejecutándose en mi Oracle JDK 10.0.1:
2014-08-18T11:25:26+02:00
2014-08-18T11:25:26+02:00[Europe/Berlin]
Por otra parte, no puede existir una buena solución.
java.time
, la moderna API de fecha y hora de Java, nos permite especificar los textos que se utilizarán para los campos, tanto para el formateo como para el análisis. Así que aprovecho eso tanto para el día de la semana como para el mes, especificando las abreviaturas sin punto que se usaron con los datos locales antiguos de COMPAT o JRE. He utilizado los Map.of
Java 9 Map.of
y Map.ofEntries
para crear los mapas que necesitamos. Si esto también funciona en Java 8, debe encontrar otra forma de rellenar los dos mapas, confío en que lo haga.
Si necesita un java.util.Date
antiguo (probablemente en una base de código heredado), conviértalo de esta manera:
Date date = Date.from(odt.toInstant());
System.out.println("As legacy Date: " + date);
Salida en mi zona horaria (Europa / Copenhague, probablemente más o menos de acuerdo con la tuya):
As legacy Date: Mon Aug 18 11:25:26 CEST 2014
Sugerencia para una estrategia.
Estoy pensando que si ese fuera yo, consideraría proceder de esta manera:
- Espera Establezca la propiedad del sistema relevante desde Java:
System.setProperty("java.locale.providers", "COMPAT,CLDR");
Para que no se olvide en ningún entorno. Los datos de configuración regional de COMPAT han existido desde la versión 1.0 (creo que, al menos cerca), por lo que una gran cantidad de código depende de ella (no solo la suya). El nombre se cambió de JRE a COMPAT en Java 9. Para mí, esto puede sonar como un plan para mantener los datos durante bastante tiempo. De acuerdo con la documentación de acceso anticipado , todavía estará disponible en Java 11 (la próxima versión de Java de "soporte a largo plazo") y sin advertencia de desaprobación o similar. Y si se eliminara en una futura versión de Java, probablemente podrá descubrir lo suficientemente pronto para poder solucionar el problema antes de actualizar. - Usa mi solución de arriba .
- Utilice java.util.spi.LocaleServiceProvider que Basil Bourque enlazó. No hay duda de que esta es una buena solución en caso de que los datos de COMPAT se eliminen en un momento desconocido en el futuro. Incluso puede copiar los datos locales de COMPAT en sus propios archivos para que no puedan quitárselos, solo verifique si hay problemas de derechos de autor antes de hacerlo. La razón por la que menciono la buena solución al final es que dijo que no está satisfecho con tener que establecer una propiedad del sistema en todos los entornos posibles donde pueda ejecutarse su programa. Por lo que puedo decir, el uso de sus propios datos de configuración regional a través de la interfaz del proveedor de servicios de configuración regional aún requerirá que configure la misma propiedad del sistema (solo con un valor diferente).
Solo por mencionar: SimpleDateFormat
es una forma antigua de formatear fechas en las que BTW no es seguro para subprocesos. Desde Java 8, hay nuevos paquetes llamados java.time
y java.time.format
y deberías usarlos para trabajar con fechas. Para tus propósitos debes usar la clase DateTimeFormatter .