Dominando la Clase DateTimeFormatter en Java 8
En este tutorial, revisaremos la clase DateTimeFormatter de Java 8 y sus patrones de formateo. También discutiremos los posibles casos de uso para esta clase. La clase DateTimeFormatter nos permite formatear fechas y horas de manera uniforme en una aplicación usando patrones predefinidos o definidos por el usuario.
La clase DateTimeFormatter viene con múltiples formatos de fecha/hora predefinidos que siguen estándares ISO y RFC. Por ejemplo, podemos usar la instancia ISO_LOCAL_DATE para analizar una fecha como ‘2018-03-09’:
DateTimeFormatter.ISO_LOCAL_DATE.format(LocalDate.of(2018, 3, 9));
DateTimeFormatter.ISO_OFFSET_DATE.format(LocalDate.of(2018, 3, 9).atStartOfDay(ZoneId.of("UTC-3")));
DateTimeFormatter.RFC_1123_DATE_TIME.format(LocalDate.of(2018, 3, 9).atStartOfDay(ZoneId.of("UTC-3")));
LocalDate.from(DateTimeFormatter.ISO_LOCAL_DATE.parse("2018-03-09")).plusDays(3);
A veces, queremos imprimir fechas de manera más legible. En tales casos, podemos usar el enumerado java.time.format.FormatStyle (FULL, LONG, MEDIUM, SHORT) con nuestro DateTimeFormatter:
LocalDate anotherSummerDay = LocalDate.of(2016, 8, 23);
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(anotherSummerDay));
Tuesday, August 23, 2016
August 23, 2016
Aug 23, 2016
8/23/16
LocalDate anotherSummerDay = LocalDate.of(2016, 8, 23);
LocalTime anotherTime = LocalTime.of(13, 12, 45);
ZonedDateTime zonedDateTime = ZonedDateTime.of(anotherSummerDay, anotherTime, ZoneId.of("Europe/Helsinki"));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
.format(zonedDateTime));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
.format(zonedDateTime));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.format(zonedDateTime));
System.out.println(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.format(zonedDateTime));
Tuesday, August 23, 2016 1:12:45 PM EEST
August 23, 2016 1:12:45 PM EEST
Aug 23, 2016 1:12:45 PM
8/23/16 1:12 PM
ZonedDateTime dateTime = ZonedDateTime.from(
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
.parse("Tuesday, August 23, 2016 1:12:45 PM EET"));
System.out.println(dateTime.plusHours(9));
Los formatos predefinidos y los estilos integrados pueden cubrir muchas situaciones. Sin embargo, a veces necesitamos formatear una fecha y una hora de manera diferente. Aquí es donde entran en juego los patrones de formateo personalizados.
Supongamos que queremos presentar un objeto java.time.LocalDate usando un formato europeo regular como 31.12.2018. Para hacer esto, podríamos llamar al método de fábrica DateTimeFormatter.ofPattern(“dd.MM.yyyy”).
String europeanDatePattern = "dd.MM.yyyy";
DateTimeFormatter europeanDateFormatter = DateTimeFormatter.ofPattern(europeanDatePattern);
System.out.println(europeanDateFormatter.format(LocalDate.of(2016, 7, 31)));
Symbol Meaning Presentation Examples
------ ------- ------------ -------
u year year 2004; 04
y year-of-era year 2004; 04
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
DateTimeFormatter europeanDateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
System.out.println(LocalDate.from(europeanDateFormatter.parse("15.08.2014")).isLeapYear());
También hay letras de patrón que se pueden usar para patrones de tiempo:
Symbol Meaning Presentation Examples
------ ------- ------------ -------
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
n nano-of-second number 987654321
String timeColonPattern = "HH:mm:ss";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50);
System.out.println(timeColonFormatter.format(colonTime));
String timeColonPattern = "HH:mm:ss SSS";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50).plus(329, ChronoUnit.MILLIS);
System.out.println(timeColonFormatter.format(colonTime));
String timeColonPattern = "hh:mm:ss a";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50);
System.out.println(timeColonFormatter.format(colonTime));
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss a");
System.out.println(LocalTime.from(timeFormatter.parse("12:25:30 AM")).isBefore(LocalTime.NOON));
A menudo queremos ver una zona horaria de alguna variable de fecha-hora específica. Si usamos una fecha-hora basada en Nueva York (UTC -4), podemos usar el patrón de letra “z” para el nombre de la zona horaria:
String newYorkDateTimePattern = "dd.MM.yyyy HH:mm z";
DateTimeFormatter newYorkDateFormatter = DateTimeFormatter.ofPattern(newYorkDateTimePattern);
LocalDateTime summerDay = LocalDateTime.of(2016, 7, 31, 14, 15);
System.out.println(newYorkDateFormatter.format(ZonedDateTime.of(summerDay, ZoneId.of("UTC-4"))));
DateTimeFormatter zonedFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm z");
System.out.println(ZonedDateTime.from(zonedFormatter.parse("31.07.2016 14:15 GMT+02:00")).getOffset().getTotalSeconds());
No solo es posible usar una zona específica y obtener un horario correcto, sino también producir un formateo de fecha que utilizaría el formato de una localidad específica. Verifiquemos esto con la localidad de EE.UU.:
LocalDate date = LocalDate.of(2023, 9, 18);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd, yy: EEE").withLocale(Locale.US);
String formattedDate = date.format(formatter);
Sep 18, 23: Mon
LocalDate date = LocalDate.of(2023, 9, 18);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM dd, yyyy: EEEE").withLocale(Locale.US);
String formattedDate = date.format(formatter);
September 18, 2023: Monday
9월 18, 23: 월
9월 18, 2023: 월요일
DateTimeFormatter viene con un gran formateador ISO instantáneo llamado ISO_INSTANT. Como su nombre implica, este formateador proporciona una forma conveniente de formatear o analizar un instante en UTC.
@Test(expected = UnsupportedTemporalTypeException.class)
public void shouldExpectAnExceptionIfInputIsLocalDateTime() {
DateTimeFormatter.ISO_INSTANT.format(LocalDateTime.now());
}
@Test
public void shouldPrintFormattedZonedDateTime() {
ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 02, 15, 0, 0, 0, 0, ZoneId.of("Europe/Paris"));
String formattedZonedDateTime = DateTimeFormatter.ISO_INSTANT.format(zonedDateTime);
Assert.assertEquals("2021-02-14T23:00:00Z", formattedZonedDateTime);
}
@Test
public void shouldParseZonedDateTime() {
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault());
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2021-10-01T05:06:20Z", formatter);
Assert.assertEquals("2021-10-01T05:06:20Z", DateTimeFormatter.ISO_INSTANT.format(zonedDateTime));
}
@Test(expected = DateTimeParseException.class)
public void shouldExpectAnExceptionIfTimeZoneIsMissing() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2021-11-01T05:06:20Z", DateTimeFormatter.ISO_INSTANT);
}
Java 19 introduce un nuevo método ofLocalizedPattern(String), cuyo nombre podría resultar engañoso, así que vale la pena mencionarlo aquí. El propósito de este método no es proporcionar formateo localizado, como vimos en los ejemplos anteriores con ofPattern(String) combinado con localidad.
En este artículo, discutimos cómo usar la clase DateTimeFormatter para formatear fechas y horas. También examinamos ejemplos de patrones que son comunes cuando trabajamos con instancias de fecha-hora.