¿`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
-
Para el 99% de los propósitos, puede lanzar una moneda, no habrá diferencia si usa
yyyy
ouuuu
(o si usayy
ouu
para un año de 2 dígitos). - 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
.