Biblioteca estándar
Fechas y horas con java.time
Elige entre fechas locales e instantes, construye valores válidos y trabaja con un reloj controlado sin perderte con la zona.
Desde Java 8
Este capítulo cubre Java 8. Estás leyendo en .
Primero, qué dato quieres guardar
Un cumpleaños no es un instante universal. Una alarma a las ocho tampoco identifica por sí sola
un momento. Antes de llamar a un método, decide qué información tiene tu dato.
java.time, disponible desde Java 8, separa esas ideas en tipos distintos.
| Dato | Tipo | Ejemplo |
|---|---|---|
| Día de calendario, sin hora | LocalDate | Fecha de nacimiento |
| Hora del día, sin fecha | LocalTime | Apertura de una tienda |
| Fecha y hora, sin zona | LocalDateTime | Hora local propuesta para una cita |
| Punto en la línea temporal | Instant | Momento en que se recibió un pago |
El prefijo Local significa que no hay zona ni desplazamiento respecto a UTC.
No quiere decir «la zona de tu ordenador». Un LocalDateTime no puede decirte si una
cita ocurre antes que un evento guardado como Instant sin añadir más información.
De calendario a momento concreto
-
01
Día y hora
LocalDate + LocalTimeDos valores que describen lo que marca un calendario y un reloj. -
02
Fecha y hora local
LocalDateTimeJuntarlos sigue sin aportar una zona horaria. -
03
Instante
InstantNecesitas resolver la zona o aportar un offset para llegar aquí.
Construir sin depender del reloj
Usa of para campos separados y parse para el formato ISO que admite cada tipo.
En estas fábricas los meses van de 1 a 12; Month evita números poco expresivos.
Todos los programas de este capítulo son compatibles con Java 8.
Guarda este primero como LocalValues.java:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
public class LocalValues {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2024, Month.FEBRUARY, 29);
LocalTime time = LocalTime.of(9, 30);
LocalDateTime appointment = date.atTime(time);
System.out.println(date);
System.out.println(time);
System.out.println(appointment);
System.out.println(appointment.toLocalDate().equals(date));
System.out.println(LocalDate.parse("2024-02-29").getDayOfWeek());
}
}Salida:
2024-02-29
09:30
2024-02-29T09:30
true
THURSDAYEl texto ISO coloca primero el año. toString() sirve para inspeccionar el valor,
no para decidir el formato que verá una persona en tu aplicación.
getDayOfWeek() devuelve un enum, no un nombre traducido al castellano.
Inmutables: transformar devuelve otro valor
Las clases principales de java.time son inmutables. Una llamada a plusDays o withHour
no modifica el receptor: devuelve un resultado. Si lo ignoras, no has actualizado tu variable.
Esto permite compartir un valor sin que otro código cambie sus campos a escondidas.
import java.time.LocalDate;
public class ImmutableDates {
public static void main(String[] args) {
LocalDate start = LocalDate.of(2024, 2, 28);
start.plusDays(1);
LocalDate next = start.plusDays(1);
System.out.println(start);
System.out.println(next);
System.out.println(start.isBefore(next));
System.out.println(next.equals(LocalDate.of(2024, 2, 29)));
}
}Salida:
2024-02-28
2024-02-29
true
trueCompara valores con equals, isBefore o isAfter, no con ==.
Que dos referencias apunten al mismo objeto no es la pregunta que te interesa aquí.
Una variable puede reasignarse al resultado; la inmutabilidad pertenece al objeto,
no convierte automáticamente tu variable en final.
Los campos inválidos no se normalizan en silencio
LocalDate.of(2023, 2, 29) no se convierte en marzo: lanza DateTimeException.
LocalTime.of(24, 0) también falla; la medianoche se representa como 00:00.
Los segundos válidos de LocalTime van de 0 a 59.
Para comprobarlo sin depender del texto del mensaje de la excepción:
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
public class InvalidFields {
public static void main(String[] args) {
try {
LocalDate.of(2023, 2, 29);
} catch (DateTimeException ex) {
System.out.println("Fecha inválida");
}
try {
LocalTime.of(24, 0);
} catch (DateTimeException ex) {
System.out.println("Hora inválida");
}
}
}Salida:
Fecha inválida
Hora inválidaInstant: cuándo ocurrió algo
Instant representa un punto en la línea temporal mediante segundos desde el origen
1970-01-01T00:00:00Z y una fracción de nanosegundo. La Z del texto ISO indica UTC.
No almacena la zona de una ciudad ni una presentación local del evento.
Instant.now() consulta el reloj del sistema. LocalDate.now() consulta además la zona
predeterminada para decidir qué día es. Ninguna de esas llamadas tiene una salida fija.
Si ejecutas una prueba cerca de medianoche, el día puede cambiar entre dos llamadas.
Un reloj explícito para probar
Pasa un Clock a tu código cuando necesites «ahora». En producción puedes proporcionar
Clock.systemUTC() o Clock.system(zone); en una prueba, Clock.fixed.
La zona del reloj se usa al pedir una fecha local; no cambia el instante fijado.
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
public class FixedClock {
public static void main(String[] args) {
Instant event = Instant.parse("2024-01-01T00:30:00Z");
Clock utc = Clock.fixed(event, ZoneOffset.UTC);
Clock west = utc.withZone(ZoneOffset.ofHours(-1));
System.out.println(Instant.now(utc));
System.out.println(LocalDate.now(utc));
System.out.println(LocalDate.now(west));
System.out.println(Instant.now(utc).equals(Instant.now(west)));
}
}Salida:
2024-01-01T00:30:00Z
2024-01-01
2023-12-31
trueAquí el resultado es estable porque has fijado tanto el instante como los offsets.
Un Instant no garantiza que el reloj físico tenga precisión de nanosegundos, y el reloj
civil del sistema puede corregirse. Para medir rendimiento, usa System.nanoTime() y
resta dos lecturas; no lo conviertas en una fecha.
Elegir en una aplicación real
Guarda una fecha de nacimiento como LocalDate: inventarle medianoche y UTC cambia el modelo.
Para una recepción de mensaje, guarda un Instant. Para una cita futura ligada a un lugar,
conserva también la zona y la intención local: sus reglas pueden cambiar por decisiones legales.
Evita empezar código nuevo con Date y Calendar si puedes usar java.time.
Si integras una API antigua, Date.toInstant() y Date.from(instant) sirven de puente;
Date trabaja con milisegundos, por lo que la conversión puede perder precisión.
Qué te llevas
- Elige el tipo según el significado: día, hora, fecha y hora local o instante.
Localno añade la zona del equipo ni identifica por sí solo un momento universal.- Las transformaciones devuelven valores; conserva el resultado y compara con
equals. ofrechaza campos inválidos; el reloj y la zona predeterminados no dan salidas fijas.- Inyecta un
Clockpara probar «ahora» y separa fechas civiles de mediciones de rendimiento.