Trabajo avanzado con fechas y horas en Java: desde LocalDate hasta manipulaciones complejas
En Java moderno (a partir de la versión 8), el estándar de facto para trabajar con fechas y horas es el paquete java.time. Reemplazó a los obsoletos java.util.Date y java.util.Calendar, ofreciendo objetos inmutables, seguridad en hilos y una API intuitiva. En este artículo analizaremos técnicas avanzadas: trabajo con zonas horarias, cálculos precisos de diferencias, ajustadores personalizados y formateo complejo.
1. Manipulaciones con fechas mediante TemporalAdjusters
Una de las capacidades más potentes de java.time es la interfaz TemporalAdjuster. Permite realizar ajustes complejos de fechas — por ejemplo, encontrar el «próximo martes» o el «último día del mes».
La clase TemporalAdjusters (nótese el plural) contiene métodos estáticos listos para usar:
firstDayOfMonth()— primer día del mes;lastDayOfMonth()— último día del mes;next(DayOfWeek.FRIDAY)— próximo día de la semana especificado;previousOrSame(DayOfWeek.MONDAY)— lunes anterior o el mismo.
Ejemplo: encontremos la fecha del próximo miércoles después del 15 de marzo de 2024.
import java.time.LocalDate;import java.time.DayOfWeek;import java.time.temporal.TemporalAdjusters;
public class TemporalExample { public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 3, 15); LocalDate nextWednesday = date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY)); System.out.println("Próximo miércoles: " + nextWednesday); // Salida: 2024-03-20 }}Puede crear sus propios ajustadores implementando la interfaz funcional TemporalAdjuster. Esto es especialmente útil para la lógica de negocio: «primer día laborable del mes» o «último viernes del trimestre».
2. Trabajo con zonas horarias y ZonedDateTime
Las aplicaciones globales deben considerar las zonas horarias. La clase ZonedDateTime combina fecha, hora y zona (ZoneId).
Ejemplo: conversión de hora de Nueva York a Berlín teniendo en cuenta el horario de verano/invierno.
import java.time.ZonedDateTime;import java.time.ZoneId;import java.time.format.DateTimeFormatter;
public class ZoneExample { public static void main(String[] args) { ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York")); ZonedDateTime berlin = newYork.withZoneSameInstant(ZoneId.of("Europe/Berlin"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm z"); System.out.println("Nueva York: " + newYork.format(formatter)); System.out.println("Berlín: " + berlin.format(formatter)); }}Importante: para obtener todas las zonas disponibles use ZoneId.getAvailableZoneIds(). Evite las abreviaturas de tres letras (EST, PST) — son ambiguas. Use siempre el formato continente/ciudad (por ejemplo, Asia/Tokyo).
3. Cálculos precisos de diferencias: ChronoUnit y Duration
Para calcular la diferencia entre fechas en Java hay dos enfoques:
ChronoUnit— para diferencias en unidades de calendario (días, meses, años);Duration— para intervalos de tiempo precisos (segundos, nanosegundos).
Ejemplo: ¿cuántos días y meses faltan para Año Nuevo?
import java.time.LocalDate;import java.time.temporal.ChronoUnit;
public class DiffExample { public static void main(String[] args) { LocalDate today = LocalDate.now(); LocalDate newYear = LocalDate.of(today.getYear() + 1, 1, 1);
long days = ChronoUnit.DAYS.between(today, new