Java LocalTime
Representa horas sin fechas ni zonas horarias en Java con LocalTime.
LocalTime es el espejo de LocalDate: una hora del día — hora, minuto, segundo, nanosegundo — sin fecha y sin zona horaria. Representa la misma lectura del reloj en cualquier día del calendario en cualquier lugar: LocalTime.of(9, 30) son las nueve y media en cualquier día en cualquier ciudad.
Ese es el tipo correcto para una hora recurrente del día — una tienda que abre a las 09:30, un trabajo tipo cron diario a las 03:15, una reunión que comienza a las 14:00 independientemente de la fecha. No es el tipo correcto para "el momento en que el usuario hizo clic en enviar a las 14:30 de hoy" — eso necesita una fecha y probablemente una zona. Los dos combinados forman un LocalDateTime, el tema del próximo capítulo.
Creación
LocalTime now = LocalTime.now(); // current time in the JVM default zone
LocalTime nine = LocalTime.of(9, 0); // hour, minute
LocalTime nineThirty = LocalTime.of(9, 30, 15); // hour, minute, second
LocalTime nineThirtyNanos = LocalTime.of(9, 30, 15, 500_000_000); // + nanosecond (0 ..999_999_999)
LocalTime parsed = LocalTime.parse("09:30:15"); // ISO-8601 HH:mm[:ss[.nnnnnnnnn]]Las constantes predefinidas son útiles para condiciones de límite:
LocalTime.MIDNIGHT // 00:00
LocalTime.NOON // 12:00
LocalTime.MIN // 00:00:00.000000000
LocalTime.MAX // 23:59:59.999999999MIN y MAX son especialmente útiles cuando se combinan con un LocalDate para abarcar un día completo: LocalDateTime.of(date, LocalTime.MIN) es "medianoche al inicio de la fecha"; LocalTime.MAX es el último nanosegundo representable del día.
Resolución: nanosegundos
LocalTime es preciso hasta el nanosegundo — resolución de nueve campos (1 segundo = 1.000.000.000 ns). En la mayoría de los sistemas operativos la resolución real del reloj es de milisegundos (1.000.000 ns cada uno) o microsegundos (1.000 ns cada uno); la precisión extra está ahí para que el tipo no pierda información al interactuar con sistemas que tienen relojes de mayor resolución.
Accesores directos:
time.getHour(); // 0-23
time.getMinute(); // 0-59
time.getSecond(); // 0-59
time.getNano(); // 0-999_999_999No existe getMilli(); si quieres milisegundos, divide el nano: time.getNano() / 1_000_000.
24 horas, sin AM/PM
LocalTime es internamente de 24 horas. LocalTime.of(13, 0) es "la 1 PM" y no existe AM/PM en el tipo. Analizar cadenas con "AM"/"PM" requiere un DateTimeFormatter personalizado (el capítulo de Análisis de Fechas cubre esto) — el parse predeterminado es solo ISO-8601 de 24 horas.
Aritmética y modificaciones
La misma forma fluida que LocalDate:
time.plusHours(2);
time.plusMinutes(30);
time.plusSeconds(45);
time.plusNanos(500_000);
time.withHour(14); // replace one field
time.withMinute(0);El comportamiento de desbordamiento: cada método plus/minus en LocalTime da la vuelta silenciosamente a la medianoche. LocalTime.of(23, 0).plusHours(2) es 01:00, no "mañana a las 01:00" — no existe "mañana" en LocalTime. Si necesitas saber si ocurrió un desbordamiento, usa LocalDateTime o haz los cálculos tú mismo:
LocalTime late = LocalTime.of(23, 0);
LocalTime later = late.plusHours(2); // 01:00 — silently wrapped
// To detect wrap: compare the new value's getHour with what you expected, or use LocalDateTime.Este desbordamiento está documentado y es intencional, pero es un punto delicado si lo olvidas. Para cálculos de "¿cuándo termina este turno?" que pueden cruzar la medianoche, el tipo correcto es LocalDateTime, no LocalTime.
Comparación
time.isBefore(other);
time.isAfter(other);
time.compareTo(other);
time.equals(other);Ordenación lexicográfica por hora:minuto:segundo:nano. LocalTime implementa Comparable<LocalTime>, por lo que puedes ordenar una lista de horas o usarlo directamente como clave de TreeMap.
Distancia
Duration.between y ChronoUnit.X.between funcionan ambos:
Duration d = Duration.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end);
long seconds = ChronoUnit.SECONDS.between(start, end);El signo: positivo cuando end es posterior a start, negativo en caso contrario. La misma advertencia de desbordamiento aplica — Duration.between(LocalTime.of(23, 0), LocalTime.of(1, 0)) son −22 horas, no +2 horas; la API trata 01:00 como anterior a 23:00 del mismo día nocional. Para "el turno pasó la medianoche," la aritmética basada en LocalDateTime es la herramienta correcta.
Combinación con LocalDate
Con frecuencia convertirás a una fecha con hora:
LocalDate date = LocalDate.of(2025, 11, 4);
LocalTime time = LocalTime.of(9, 30);
LocalDateTime dt = date.atTime(time); // 2025-11-04T09:30
LocalDateTime dt2 = time.atDate(date); // same thingatTime / atDate son los métodos puente. El resultado es un LocalDateTime — todavía sin zona, pero ahora anclado a un día calendario. El próximo capítulo lo lleva más lejos.
Un ejemplo práctico: un pequeño ayudante de programación
El programa a continuación usa LocalTime para una tarea de tipo agenda diaria: definir una ventana de "horario de oficina", verificar si un momento dado cae dentro de ella, calcular cuánto tiempo falta hasta la próxima apertura y demostrar el problema del desbordamiento a la medianoche.
Lo que se puede extraer de la ejecución:
LocalTimeimpreso como09:00,17:30,12:30— la forma canónica ISO-8601 de 24 horas. Sin AM/PM en el tipo. Si necesitas mostrar "5:30 PM" a un usuario, el capítulo de Formato de Fechas tiene el formateador para eso; el tipo en sí no lo conoce.- La verificación "¿la hora está dentro de la ventana?" usó
!isBefore(open) && !isAfter(close). Ese es el idioma de intervalo semiabierto-vs-cerrado — ambos extremos están incluidos. Para "estrictamente dentro," cambia a las formas sin negar. Duration.between(LocalTime.of(22, 0), LocalTime.of(2, 0))devolvióPT-20H, noPT4H.LocalTimeno tiene noción de "día siguiente" — cuandoendes anterior astarten términos del reloj, la duración se vuelve negativa. Para un turno que cruza la medianoche, cambia las entradas aLocalDateTimey deja que las fechas resuelvan la ambigüedad. Este es el mayor punto problemático deLocalTime.LocalTime.of(23, 30).plusHours(2)devolvió01:30. El desbordamiento es silencioso — sin excepción, sin bandera, sin arrastre a la fecha. Si necesitas saber "¿hubo desbordamiento?", usaLocalDateTime. Si genuinamente quieres aritmética de reloj módulo 24 (una agenda recurrente, por ejemplo), el desbordamiento es el comportamiento correcto.date.atTime(time)fue el puente canónico aLocalDateTime. El espejotime.atDate(date)produce el mismo resultado. Los usarás constantemente cuando leas una hora de una fuente y una fecha de otra, y luego las combines en el único objeto que la API de destino necesita.
Qué sigue
El próximo capítulo, Java LocalDateTime, combina LocalDate y LocalTime en el tercer tipo "local": una fecha y una hora, todavía sin zona horaria adjunta. Esa es la unidad natural para "esto ocurrió a las 14:30 del 4 de noviembre" cuando la zona es irrelevante o se registra por separado.