date parsing datetime date-formatting java-time

¿`uuuu` versus` aaaa` en los códigos de patrón de formato `DateTimeFormatter` en Java?



parsing date-formatting (3)

La documentación de la clase DateTimeFormatter dice acerca de sus códigos de formato para el año:

u año año 2004; 04

y año de la era 2004; 04

...

Año: el recuento de letras determina el ancho de campo mínimo por debajo del cual se utiliza el relleno. Si el recuento de letras es dos, se usa una forma reducida de dos dígitos. Para imprimir, esto genera los dos dígitos más a la derecha. Para el análisis, se analizará utilizando el valor base de 2000, lo que dará como resultado un año dentro del rango de 2000 a 2099 inclusive. Si el recuento de letras es inferior a cuatro (pero no dos), entonces el signo solo se genera para años negativos según SignStyle.NORMAL. De lo contrario, el signo se emite si se excede el ancho del pad, según SignStyle.EXCEEDS_PAD.

Ninguna otra mención de "era".

Entonces, ¿cuál es la diferencia entre estos dos códigos, u versus y , year versus year-of-era ?

¿Cuándo debería usar algo como este patrón uuuu-MM-dd y cuándo yyyy-MM-dd cuando trabajo con fechas en Java?

Parece que ese código de ejemplo escrito por aquellos que saben usar uuuu , pero ¿por qué?

Otras clases de formato, como el SimpleDateFormat heredado, SimpleDateFormat tienen yyyy , así que estoy confundido por qué java.time trae este uuuu para "año de era".


Larga historia corta

  1. Para el 99% de los propósitos, puede lanzar una moneda, no habrá diferencia si usa yyyy o uuuu (o si usa yy o uu para un año de 2 dígitos).
  2. Depende de lo que quiera que suceda en caso de que ocurra un año antes de 1 CE (1 AD). El punto es que en el 99% de los programas ese año nunca ocurrirá.

Otras dos respuestas ya han presentado los hechos de cómo trabajas muy bien, pero todavía sentía que faltaba algo, por lo que estoy contribuyendo con una respuesta un poco más basada en la opinión.

Para formatear

Suponiendo que no espere que se formatee un año antes de 1 CE, lo mejor que puede hacer es verificar esta suposición y reaccionar adecuadamente en caso de que se rompa. Por ejemplo, dependiendo de las circunstancias y los requisitos, puede imprimir un mensaje de error o lanzar una excepción. Una ruta de falla muy suave podría ser usar un patrón con y (año de la era) y G (era) en este caso y un patrón con u o y en el caso normal de la era actual. Tenga en cuenta que si está imprimiendo la fecha actual o la fecha en que se compiló su programa, puede estar seguro de que está en la era común y puede optar por omitir el cheque.

Para analizar

En muchos (¿la mayoría?), El análisis también significa validar, lo que significa que no tiene garantías de cómo se ve su cadena de entrada. Por lo general, proviene del usuario o de otro sistema. Un ejemplo: una cadena de fecha viene como 2018-09-29. Aquí la elección entre uuuu y yyyy debe depender de lo que desee que suceda en caso de que la cadena contenga un año de 0 o negativo (por ejemplo, 0000-08-17 o -012-11-13 ). Suponiendo que esto sea un error, la respuesta inmediata es: use yyyy para que se yyyy una excepción en este caso. Aún más fino: use uuuu y después de analizar realice una verificación de rango de la fecha analizada. El último enfoque permite una validación más fina y un mejor mensaje de error en caso de un error de validación.

Caso especial (ya mencionado por Meno Hochschild): si su formateador usa un estilo de resolución estricto y contiene y sin G , el análisis siempre fallará porque, estrictamente hablando, el año de era es ambiguo sin era: 1950 podría significar 1950 CE o 1950 AEC (1950 aC) . Entonces, en este caso, necesita u (o proporcionar una era predeterminada, esto es posible a través de un DateTimeFormatterBuilder ).

Larga historia corta otra vez

La verificación explícita del rango de sus fechas, específicamente sus años, es mejor que confiar en la elección entre uuuu y yyyy para atrapar años inesperados.


Dentro del alcance de java.time , podemos decir:

  • Es más seguro usar "u" en lugar de "y" porque DateTimeFormatter insistirá en tener una era en combinación con "y" (= año de la era). Por lo tanto, usar "u" evitaría algunas posibles excepciones inesperadas en el formato / análisis estricto. Vea también esta SO-post . Otra cosa menor que mejora con el símbolo "u" en comparación con "y" es imprimir / analizar años gregorianos negativos (en el pasado).

  • De lo contrario, podemos afirmar claramente que el uso de "u" en lugar de "y" rompe los hábitos de larga data en la programación Java . Tampoco está intuitivamente claro que "u" denota cualquier tipo de año porque a) la primera letra de la palabra inglesa "year" no está de acuerdo con este símbolo yb) SimpleDateFormat ha usado "u" para un propósito diferente desde Java -7 ( SimpleDateFormat ). La confusión está garantizada para siempre.

  • También deberíamos ver que el uso de eras (símbolo "G") en el contexto de ISO es en general peligroso si consideramos fechas históricas . Si se usa "G" con "u", ambos campos no están relacionados entre sí. Y si se usa "G" con "y", entonces el formateador está satisfecho pero aún usa el calendario gregoriano proleptico cuando la fecha histórica exige calendarios y manejo de fechas diferentes.

Información de antecedentes:

Al desarrollar e integrar el JSR-310 ( java.time ), los diseñadores decidieron usar CLDR / LDML-spec como la base de símbolos de patrones en DateTimeFormatter . El símbolo "u" ya estaba definido en CLDR como año gregoriano proleptico, por lo que este significado se adoptó en el nuevo JSR-310 (pero no en SimpleDateFormat debido a razones de compatibilidad con versiones anteriores).

Sin embargo, esta decisión de seguir CLDR no fue bastante consistente porque JSR-310 también había introducido nuevos símbolos de patrón que no existían y aún no existen en CLDR, vea también este viejo CLDR-ticket . El símbolo sugerido "I" fue cambiado por CLDR a "VV" y finalmente superado por JSR-310, incluidos los nuevos símbolos "x" y "X" . Pero "n" y "N" aún no existen en CLDR, y dado que este antiguo ticket está cerrado, no está claro si CLDR lo admitirá alguna vez en el sentido de JSR-310. Además, el ticket no menciona el símbolo "p" (instrucción de relleno en JSR-310, pero no está definida en CLDR). Por lo tanto, todavía no tenemos un acuerdo perfecto entre las definiciones de patrones en diferentes bibliotecas e idiomas.

Y sobre "y": tampoco deberíamos pasar por alto el hecho de que CLDR asocia este año de era con al menos algún tipo de año mixto juliano / gregoriano y no con el año gregoriano proleptico como lo hace JSR-310 (dejando la rareza de años negativos a un lado). Por lo tanto, tampoco hay un acuerdo perfecto entre CLDR y JSR-310 aquí.


En la sección javadoc Patrones para formatear y analizar para DateTimeFormatter , enumera los siguientes 3 símbolos relevantes:

Symbol Meaning Presentation Examples ------ ------- ------------ ------- G era text AD; Anno Domini; A u year year 2004; 04 y year-of-era year 2004; 04

Solo para comparar, estos otros símbolos son lo suficientemente fáciles de entender:

D day-of-year number 189 d day-of-month number 10 E day-of-week text Tue; Tuesday; T

El day-of-year , el day-of-month y el day-of-week son obviamente el día dentro del alcance dado (año, mes, semana).

Por lo tanto, year-of-era significa el año dentro del alcance (era) dado, y justo encima de esa era se muestra con un valor de ejemplo de AD (el otro valor, por supuesto, es BC ).

year es el año firmado , donde el año 0 es 1 BC , el año -1 es 2 BC , y así sucesivamente.

Para ilustrar: ¿Cuándo fue asesinado Julio César ?

  • 15 de marzo de 44 a. C. (utilizando el patrón MMMM d, y GG )
  • 15 de marzo, -43 (usando el patrón MMMM d, u )

La distinción, por supuesto, solo importará si el año es cero o negativo, y como eso es raro, a la mayoría de las personas no les importa, aunque deberían.

Conclusión: si usa y también debe usar G Como G rara vez se usa, el símbolo del año correcto es u , no y , de lo contrario, un año no positivo se mostrará incorrectamente.

Esto se conoce como programación defensiva :

La programación defensiva es una forma de diseño defensivo destinado a garantizar la función continua de una pieza de software en circunstancias imprevistas .

Tenga en cuenta que DateTimeFormatter es coherente con SimpleDateFormat :

Letter Date or Time Component Presentation Examples ------ ---------------------- ------------ -------- G Era designator Text AD y Year Year 1996; 96

Los años negativos siempre han sido un problema, y ​​ahora lo arreglaron agregando u .