1. Introducción
En este tutorial, realizaremos un recorrido en profundidad por la clase SimpleDateFormat .
Echaremos un vistazo a los estilos sencillos de creación de instancias y formato , así como a los métodos útiles que expone la clase para manejar las configuraciones regionales y las zonas horarias .
2. Instanciación simple
Primero, veamos cómo crear una instancia de un nuevo objeto SimpleDateFormat .
Hay 4 posibles constructores, pero de acuerdo con el nombre, mantengamos las cosas simples. Todo lo que necesitamos para comenzar es una representación de cadena de un patrón de fecha que queremos .
Comencemos con un patrón de fecha separado por guiones como este:
"dd-MM-yyyy"
Esto formateará correctamente una fecha comenzando con el día actual del mes, el mes actual del año y finalmente el año actual. Podemos probar nuestro nuevo formateador con una prueba unitaria simple. Crearemos una instancia de un nuevo objeto SimpleDateFormat y pasaremos una fecha conocida:
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); assertEquals("24-05-1977", formatter.format(new Date(233345223232L)));
En el código anterior, los formateador conversos milisegundos como l ong en una fecha legible - el 24 de mayo., 1977
2.1. Métodos de fábrica
Aunque SimpleDateFormat es una clase muy útil para crear rápidamente un formateador fecha, se nos anima a usar los métodos de fábrica en el DateFormat clase GetDateFormat () , getDateTimeFormat () , GetTimeFormat () .
El ejemplo anterior se ve un poco diferente cuando se utilizan estos métodos de fábrica:
DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT); assertEquals("5/24/77", formatter.format(new Date(233345223232L)));
Como podemos ver desde arriba, el número de opciones de formato está predeterminado por los campos de la clase DateFormat . Esto restringe en gran medida nuestras opciones disponibles para formatear, por lo que nos ceñiremos a SimpleDateFormat en este artículo.
2.2. Seguridad del hilo
El JavaDoc para SimpleDateFormat establece explícitamente:
Los formatos de fecha no están sincronizados. Se recomienda crear instancias de formato independientes para cada hilo. Si varios subprocesos acceden a un formato al mismo tiempo, se debe sincronizar externamente.
Por lo tanto, las instancias de SimpleDateFormat no son seguras para subprocesos y debemos usarlas con cuidado en entornos concurrentes.
El mejor enfoque para resolver este problemaes usarlos en combinación con un ThreadLocal . De esta manera, cada hilo termina con su propia instancia SimpleDateFormat , y la falta de uso compartido hace que el programa sea seguro para subprocesos:
private final ThreadLocal formatter = ThreadLocal .withInitial(() -> new SimpleDateFormat("dd-MM-yyyy"));
El argumento del método withInitial es un proveedor de instancias de SimpleDateFormat . Cada vez que ThreadLocal necesita crear una instancia, utilizará este proveedor.
Entonces podemos usar el formateador a través de la instancia de ThreadLocal :
formatter.get().format(date)
El método ThreadLocal.get () inicializa SimpleDateFormat para el hilo actual al principio y luego reutiliza esa instancia.
Llamamos a esta técnica confinamiento de subprocesos ya que limitamos el uso de cada instancia a un subproceso específico.
Hay otros dos enfoques para abordar el mismo problema:
- Usando bloques sincronizados o ReentrantLock s
- Creación de instancias desechables de SimpleDateFormat a pedido
No se recomiendan estos dos enfoques: el primero incurre en un impacto significativo en el rendimiento cuando la contención es alta, y el segundo crea muchos objetos, lo que ejerce presión sobre la recolección de basura.
Vale la pena mencionar que, desde Java 8, se ha introducido una nueva clase DateTimeFormatter . La nueva clase DateTimeFormatter es inmutable y segura para subprocesos. Si estamos trabajando con Java 8 o posterior, se recomienda usar la nueva clase DateTimeFormatter .
3. Análisis de fechas
SimpleDateFormat y DateFormat no solo nos permiten formatear fechas, sino que también podemos revertir la operación. Usando el método de análisis , podemos ingresar la representación de cadena de una fecha y devolver el equivalente del objeto Fecha :
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); Date myDate = new Date(233276400000L); Date parsedDate = formatter.parse("24-05-1977"); assertEquals(myDate.getTime(), parsedDate.getTime());
Es importante notar aquí que el patrón proporcionado en el constructor debe tener el mismo formato que la fecha analizada usando el método de análisis .
4. Patrones de fecha y hora
SimpleDateFormat proporciona una amplia gama de opciones diferentes al formatear fechas. Si bien la lista completa está disponible en JavaDocs, exploremos algunas de las opciones más utilizadas:
Letra | Componente de fecha | Ejemplo |
---|---|---|
METRO | Mes | 12; dic |
y | año | 94 |
re | día | 23; Lun |
H | hora | 03 |
metro | minuto | 57 |
La salida devuelta por el componente de fecha también depende en gran medida del número de caracteres utilizados dentro de la Cadena . Por ejemplo, tomemos el mes de junio. Si definimos la cadena de fecha como:
"MM"
Entonces nuestro resultado aparecerá como el código numérico - 06. Sin embargo, si agregamos otra M a nuestra cadena de fecha:
"MMM"
Luego, nuestra fecha formateada resultante aparece como la palabra Jun .
5. Aplicación de configuraciones regionales
La clase SimpleDateFormat también admite una amplia gama de configuraciones regionales que se configuran cuando se llama al constructor.
Pongamos esto en práctica formateando una fecha en francés. Crearemos una instancia de un objeto SimpleDateFormat mientras proporcionamos Locale.FRANCE al constructor.
SimpleDateFormat franceDateFormatter = new SimpleDateFormat("EEEEE dd-MMMMMMM-yyyy", Locale.FRANCE); Date myWednesday = new Date(1539341312904L); assertTrue(franceDateFormatter.format(myWednesday).startsWith("vendredi"));
Al proporcionar una fecha determinada, un miércoles por la tarde, podemos afirmar que nuestro franceDateFormatter ha formateado correctamente la fecha. ¡La nueva fecha comienza correctamente con Vendredi -French para el miércoles!
Vale la pena señalar un pequeño error en la versión Locale del constructor: aunque se admiten muchas configuraciones regionales, no se garantiza la cobertura completa . Oracle recomienda utilizar los métodos de fábrica en la clase DateFormat para garantizar la cobertura de la configuración regional.
6. Cambio de zonas horarias
Dado que SimpleDateFormat extiende la clase DateFormat , también podemos manipular la zona horaria usando el método setTimeZone . Echemos un vistazo a esto en acción:
Date now = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE dd-MMM-yy HH:mm:ssZ"); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Europe/London")); logger.info(simpleDateFormat.format(now)); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York")); logger.info(simpleDateFormat.format(now));
In the above example, we supply the same Date to two different time zones on the same SimpleDateFormat object. We've also added the ‘Z' character to the end of the pattern String to indicate the time zone differences. The output from the format method is then logged for the user.
Hitting run, we can see the current times relative to the two time zones:
INFO: Friday 12-Oct-18 12:46:14+0100 INFO: Friday 12-Oct-18 07:46:14-0400
7. Summary
In this tutorial, we've taken a deep dive into the intricacies of SimpleDateFormat.
We've looked at how to instantiate SimpleDateFormat as well as how the pattern String impacts how the date is formatted.
Jugamos cambiando las configuraciones regionales de la cadena de salida antes de finalmente experimentar con el uso de zonas horarias .
Como siempre, el código fuente completo se puede encontrar en GitHub.