Saltar a contenido

Fechas en Java 8+

Actualmente en el Jdk de Java hay dos Apis para la gestión de calendarios, fechas y horas que puede usar el programador. El primero de ellos es un api obsoleto que será eliminado en un futuro de Java, y corresponde con el uso de java.util.Date, java.util.Calendar y relacionados. Estos objetos están prácticamente desde el inicio de Java y a través de la evolución del lenguaje y su uso en entornos cada vez más diversos se han quedado desfasados.

Para remediar los puntos negativos, en la versión de Jdk 8 y posteriores, el lenguaje incorpora un nuevo api para la gestión de tiempo, denominado de forma común por java.time por el paquete en el que están ubicadas las clases que lo conforman

java.time

En marzo de 2014 apareció la versión estándar de Java 8 y con ella estas nuevas clases especializadas que solucionan la mayor parte de los problemas de las clases tradicionales, e incluyen soporte automático para cosas como años bisiestos, zonas horarias y cambio automático de zona horaria

Las principales clases son

clase descripción
LocalDate representa a fechas sin la hora y nos facilita su manejo para declararlas, sumar y restar fechas y compararlas.
LocalTime es idéntica a la anterior pero para el manejo de horas, sin ninguna fecha asociada, pudiendo así compararlas, sumar o restar tiempo a las mismas
LocalDateTime como puedes suponer, es una combinación de las dos anteriores, que permite hacer lo mismo con fechas y horas simultáneamente.
Instant es muy parecida a la anterior pero a la vez muy diferente. Se usa para almacenar un punto determinado en el tiempo, o sea con fecha y hora, pero guarda su valor como un timestamp de UNIX, es decir, en nanosegundos desde el epoch de UNIX (1/1/1970 a las 00:00) y usando la zona horaria UTC. Es muy útil para manejar momentos en el tiempo de manera neutra e intercambiarlo entre aplicaciones y sistemas, por lo que lo verás utilizado muy a menudo.
ZonedDateTime esta clase es como la LocalDateTime pero teniendo en cuenta una zona horaria concreta, ya que las anteriores no la tienen en cuenta.
Period esta clase auxiliar nos ayuda a obtener diferencias entre fechas en distintos periodos (segundos, minutos, días...) y también a añadir esas diferencias a las fechas.
Duration esta es muy parecida a la anterior pero para manejo de horas exclusivamente
Clock Un reloj que proporciona acceso al instante actual, fecha y hora utilizando una zona horaria
MonthDay Un mes al día en el sistema de calendario ISO-8601, como –12-03.
OffsetDateTime Una fecha y hora con una compensación de UTC / Greenwich en el sistema de calendario ISO-8601, como 2007-12-03T10: 15: 30 + 01: 00.
Year Un año en el sistema de calendario ISO-8601, como 2007
YearMonth Un año-mes en el sistema de calendario ISO-8601, como 2007-12.
ZoneId Una identificación de zona horaria, como Europa / París
ZoneOffset Un desplazamiento de zona horaria de Greenwich / UTC, como +02: 00.

Creación de fechas

Para la creación de fechas tenemos que saber si necesitamos:

  • la fecha
  • la hora
  • la fecha y la hora

Para el manejo de fechas tenemos principalmente de 3 métodos:

  • now(): crean instancias nuevas a partir de la fecha y hora actual.
  • of(): construyen fechas y horas a partir de sus partes
  • with(): modifican la fecha u hora actual en función del parámetro que se le pase
//creación de fechas
LocalDate localDate1 = LocalDate.now();
LocalDate localDate2 = LocalDate.of(2024, 02, 20);
LocalDate localDate4 = LocalDate.of(2024, Month.FEBRUARY, 20);
LocalDate localDate3 = LocalDate.parse("2024-02-20");

//Creación de horas
LocalTime horaActual = LocalTime.now();
LocalTime horaEspecifica = LocalTime.of(15, 30, 0);
LocalTime horaParseada = LocalTime.parse("13:45:30");

//fecha/hora
LocalDateTime fechaHoraActual = LocalDateTime.now();
LocalDateTime fechaHoraEspecifica = LocalDateTime.of(2024, 4, 11, 15, 30, 0);
LocalDateTime fechaHoraParseada = LocalDateTime.parse("2024-04-11T13:45:30");

El método now() está disponible en varias clases, y su resultado es diferente

System.out.println("La fecha actual es: " + LocalDate.now());
System.out.println( "La hora actual es: " + LocalTime.now() );
System.out.println( "La fecha y hora actuales son: " + LocalDateTime.now() );
System.out.println( "El instante actual es: " + Instant.now() );
System.out.println( "La fecha y hora actuales con zona horaria son: " + ZonedDateTime.now() );
/*
La fecha actual es: 2024-04-11
La hora actual es: 13:04:16.928467900
La fecha y hora actuales son: 2024-04-11T13:04:16.928467900
El instante actual es: 2024-04-11T11:04:16.928467900Z
La fecha y hora actuales con zona horaria son: 2024-04-11T13:04:16.928467900+02:00[Europe/Madrid]

*/
Al convertirlas a cadena para mostrarlas se generan en el formato ISO 8601, que es un estándar ampliamente aceptado

Partes de Fecha/hora

Tenemos variedad de método que permiten acceder a las partes de una fecha/hora

fecha

LocalDateTime fechaHoraActual = LocalDateTime.now();

int year=fechaHoraActual.getYear();
int mes=fechaHoraActual.getMonthValue();

Modificar fechas/horas

Según la clase que manejemos tendremos una serie de métodos para añadir o quitar intervalos al dato

Método Descripción
plusDays() / minusDays() para sumar o restar días a la fecha
plusWeeks() / minusWeeks() para sumar o restar semanas
plusMonths() / minusMonths() para sumar o restar meses
plusYears() / minusYears() para sumar o restar años
System.out.println("La fecha dentro de 10 días: " + LocalDate.now().plusDays(10) );
System.out.println("La fecha dentro de 2 semanas: " + LocalDate.now().plusWeeks(2) );
System.out.println("La fecha y hora de hace 32 horas: " + LocalDateTime.now().minusHours(32) );

Ajustadores temporales

Tenemos una clase muy útil TemporalAdjusters que permite obtener nuevas fechas con ajustes muy utilizados

LocalDate fechaActual = LocalDate.now();
LocalDate ultimoDiaMesActual = fechaActual.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("Último día del mes actual: " + ultimoDiaMesActual);

LocalDate primerDiaAnioSiguiente = fechaActual.with(TemporalAdjusters.firstDayOfNextYear());
System.out.println("Primer día del año siguiente: " + primerDiaAnioSiguiente);

Tiempo transcurrido entre fechas y horas

Otra tarea habitual que necesitaremos hacer es obtener la diferencia entre dos fechas u horas, o sea, el tiempo transcurrido entre dos instantes de tiempo.

Por ejemplo, imaginemos que queremos saber cuánto tiempo ha transcurrido entre la fecha de tu nacimiento y el día de hoy. Para averiguarlo sólo hay que hacer algo como esto:

LocalDate fNacimiento = LocalDate.of(1982, Month.MAY, 23);
System.out.println("Tu edad es de " +
  ChronoUnit.YEARS.between(fNacimiento, LocalDate.now())
  + " años."
);

Otros ejemplos

LocalDateTime fechaInicio = LocalDateTime.of(2024, 4, 1, 8, 0); // 1 de abril de 2024, 08:00
LocalDateTime fechaFin = LocalDateTime.of(2024, 4, 10, 16, 30); // 10 de abril de 2024, 16:30

long diasEntre = ChronoUnit.DAYS.between(fechaInicio, fechaFin);
long horasEntre = ChronoUnit.HOURS.between(fechaInicio, fechaFin);

System.out.println("Días entre las fechas: " + diasEntre);
System.out.println("Horas entre las fechas: " + horasEntre);
Por otro lado, tenemos la clase Period para medir períodos de tiempo entre fechas

LocalDate fechaInicio = LocalDate.of(2023, 12, 25); // 25 de diciembre de 2023
LocalDate fechaFin = LocalDate.of(2024, 1, 7); // 7 de enero de 2024

Period periodo = Period.between(fechaInicio, fechaFin);

int anios = periodo.getYears();
int meses = periodo.getMonths();
int dias = periodo.getDays();

System.out.println("Años: " + anios + ", Meses: " + meses + ", Días: " + dias);

Parseo de fechas

Para interpretar una fecha desde una cadena tenemos el método parse() que si el formato cumple la ISO 8601 no hay que indicarle nada, en otro caso hay que utilizar la clase DateTimeFormatter

LocalDate fecha1 = LocalDate.parse("2020-07-06");
LocalDate fecha2 = LocalDate.parse("6/11/2020", DateTimeFormatter.ofPattern("d/M/yyyy") );

Formato personalizado de fechas

Al igual que podemos leer una fecha en el formato que necesitemos, podemos mostrar la fecha en el formato que queramos

Por defecto se muestra con el formato ISO 8601, podemos cambiar este formato como sigue

LocalDateTime fechaConHora = LocalDateTime.now();
DateTimeFormatter esDateFormat = DateTimeFormatter.ofPattern("dd/MM/yyyy hh:mm:ss");
System.out.println("Formato español (manual): " + fechaConHora.format(esDateFormat));
Existen diversos formateadores ya predefinidos en forma de constantes de DateTimeFormatter

fecha

Podemos definir un formato específico para un idioma concreto mediate Locale

Teniendo en cuenta que por ejemplo, supongamos que queremos que el mes se muestre con su nombre completo y no con un número; para ello usaríamos el formato "MMMM" (las 4 M representan el nombre del mes). Por defecto ese nombre se mostraría en inglés, por lo que veríamos por pantalla, por ejemplo "July" y no "Julio", en español

DateTimeFormatter esDateFormatLargo =
DateTimeFormatter
  .ofPattern("EEEE, dd 'de' MMMM 'de' yyyy 'a las' hh:mm:ss")
  .withLocale(new Locale("es", "ES"));
System.out.println("Formato español (largo, localizado): " + fechaConHora.format(esDateFormatLargo));
//Formato español (largo, localizado): jueves, 11 de abril de 2024 a las 01:52:55

Pero puede ser necesario que la aplicación sea global y queramos que se muestre en el formato configurado en el sistema operativo donde se ejecuta

LocalDateTime fechaHoraActual = LocalDateTime.now();

// Obtener el formato del sistema operativo actual
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);

// Formatear la fecha y hora actual según el formato del sistema operativo
String fechaFormateada = fechaHoraActual.format(formatter);

// Mostrar la fecha y hora formateada
System.out.println("Fecha y hora formateadas según el formato del sistema operativo actual: "
                   + fechaFormateada);

//Fecha y hora formateadas según el formato del sistema operativo actual: 11 abr 2024 13:52:55