W3docs

Java LocalDateTime

Combina fecha y hora sin información de zona horaria en Java con LocalDateTime.

LocalDateTime es LocalDate y LocalTime combinados — una fecha de calendario y una hora del día, con aún ninguna zona horaria. Es el valor natural para "este evento ocurrió a las 14:30 del 4 de noviembre", y es el tipo java.time más utilizado en bases de código reales que no tratan con usuarios globales.

La API fluida tiene la misma forma que las dos mitades — las mismas factorías now/of/parse, los mismos modificadores plusX/minusX/withX, las mismas comparaciones isBefore/isAfter. Las partes interesantes son las nuevas: combinar con una fecha o una hora, descomponer de vuelta, y la conversión explícita a ZonedDateTime cuando finalmente importa la zona.

Creación

LocalDateTime now    = LocalDateTime.now();                  // JVM default zone
LocalDateTime made   = LocalDateTime.of(2025, 11, 4, 14, 30);
LocalDateTime made2  = LocalDateTime.of(2025, Month.NOVEMBER, 4, 14, 30, 15);
LocalDateTime made3  = LocalDateTime.of(LocalDate.of(2025, 11, 4), LocalTime.of(14, 30));
LocalDateTime parsed = LocalDateTime.parse("2025-11-04T14:30:00");   // ISO-8601

La forma ISO-8601 tiene una T literal entre la fecha y la hora — ese es el separador estándar. LocalDateTime.parse("2025-11-04 14:30:00") (espacio en lugar de T) no se analiza con el formato predeterminado; necesitarías un DateTimeFormatter personalizado para eso.

Descomposición

LocalDate date = dt.toLocalDate();
LocalTime time = dt.toLocalTime();

Estos son el inverso de LocalDate.atTime(time) / LocalTime.atDate(date). Ambos son proyecciones puras — sin pérdida de información, sin zona introducida.

Todos los accesores de una vez

LocalDateTime hereda el menú de accesores de ambas mitades:

dt.getYear();          dt.getMonth();        dt.getMonthValue();
dt.getDayOfMonth();    dt.getDayOfWeek();    dt.getDayOfYear();
dt.getHour();          dt.getMinute();       dt.getSecond();          dt.getNano();

Mismos nombres, misma semántica. No es necesario llamar a toLocalDate() o toLocalTime() para acceder a las piezas individuales — todas están disponibles directamente.

Aritmética a través del límite

La diferencia crucial respecto a LocalTime: LocalDateTime.plusHours(3) a las 23:00 no envuelve silenciosamente. Avanza al día siguiente:

LocalDateTime late = LocalDateTime.of(2025, 11, 4, 23, 0);
late.plusHours(3);   // 2025-11-05T02:00 — date advanced as expected

Esa es la razón principal para usar LocalDateTime en lugar de LocalTime para cualquier cálculo que pueda cruzar la medianoche. Las matemáticas son consistentes con lo que esperarías de un reloj real que sabe qué día es.

dt.plusDays(7);          dt.plusHours(36);        dt.plusMinutes(150);
dt.minusYears(1);        dt.minusSeconds(45);

dt.withYear(2026);       dt.withHour(0);          dt.withMinute(0);

La regla de truncamiento de plusMonths de LocalDate también aplica aquí: LocalDateTime.of(2025, 1, 31, 12, 0).plusMonths(1) es 2025-02-28T12:00, no 2025-03-03T12:00. El truncamiento es solo en el componente de fecha; la hora no cambia.

La zona falta a propósito

Un LocalDateTime no es un momento en la línea de tiempo global. LocalDateTime.of(2025, 11, 4, 9, 0) podría ser las 9 AM en Nueva York, las 9 AM en Berlín, o las 9 AM en Tokio — tres Instants muy diferentes, y LocalDateTime no te dirá cuál. Si dos LocalDateTimes son iguales, eso significa que las cadenas de fecha y hora son iguales; no significa que los momentos subyacentes sean iguales.

Eso es una característica, no un error. Para "el contrato se firma a las 14:00 hora local donde esté el firmante," LocalDateTime es exactamente la forma correcta. Para "el servidor recibió la solicitud a las...", es la forma incorrecta — usa Instant. Para "la reunión comienza a las 14:00 hora de Nueva York," incorrecto de nuevo — usa ZonedDateTime.

Para convertir a un momento con zona, tienes que agregar la zona explícitamente:

ZonedDateTime ny = ldt.atZone(ZoneId.of("America/New_York"));
Instant       inst = ldt.atZone(ZoneId.systemDefault()).toInstant();

El atZone(...) es la llamada fundamental — es el momento en que el sistema de tipos te obliga a decidir qué zona quieres decir. Una vez decidido, la conversión a Instant es mecánica. Los siguientes dos capítulos (ZonedDateTime, Instant) cubren las formas con zona y global en detalle.

Comparación

dt.isBefore(other);
dt.isAfter(other);
dt.isEqual(other);
dt.compareTo(other);

El ordenamiento es lexicográfico por (fecha, hora). La misma advertencia de antes: dos LocalDateTimes se comparan por sus representaciones de cadena de fecha y hora, no por los momentos subyacentes — porque no hay momentos subyacentes sin una zona.

Distancia

ChronoUnit.X.between funciona directamente:

long minutes = ChronoUnit.MINUTES.between(start, end);
long days = ChronoUnit.DAYS.between(start, end);
Duration d = Duration.between(start, end);

Duration.between funciona con LocalDateTime (funciona con cualquier Temporal). Para aritmética puramente de calendario — "¿cuántos meses entre estos dos LocalDateTimes?" — usa ChronoUnit.MONTHS.between, que devuelve un long, o Period.between(start.toLocalDate(), end.toLocalDate()) para el desglose en forma de calendario.

Un ejemplo práctico: programación a través de la medianoche

El programa a continuación usa LocalDateTime para un pequeño fragmento de código de programación: un turno nocturno que comienza a las 22:00 y termina a las 06:00, calculando la duración correctamente a través de la medianoche; redondeando el "ahora" hacia arriba al siguiente cuarto de hora; encontrando la próxima ocurrencia de una reunión recurrente a las 09:30; y demostrando la regla de que la zona debe agregarse explícitamente al convertir a un momento en el tiempo.

java— editable, runs on the server

Qué extraer de la ejecución:

  • Duration.between(startShift, endShift) produjo PT8H. El turno cruzó la medianoche, y los componentes de fecha llevaron el acarreo — no hubo ambigüedad. El mismo cálculo con LocalTimes simples habría devuelto PT-16H (el problema de LocalTime del capítulo anterior). Para aritmética que puede cruzar la medianoche, LocalDateTime es el tipo adecuado.
  • Comenzando desde las 20:00, plusHours(3) permaneció en 11-04 (23:00, sin cruzar la medianoche); plusHours(5) avanzó a 11-05T01:00. La familia plus/minus en LocalDateTime propaga los acarreos correctamente a través de toda la cadena A/M/D/h/m/s/ns. No se requiere tratamiento especial en tu código.
  • El bloque "siguiente reunión a las 09:30" construyó las 09:30 de hoy con tres llamadas withX, y luego eligió entre hoy y mañana basándose en isBefore. Esta es la forma común para "el próximo evento recurrente a esta hora del día" — suficientemente pequeño para incluirlo en línea, suficientemente común para extraerlo en un método auxiliar si tienes muchos.
  • El bloque "mismo LocalDateTime, zonas diferentes" produjo dos Instants distintos separados por seis horas. Esa es la razón central por la que LocalDateTime no pretende ser un momento. La clase se niega a fingir que una fecha y hora solas son suficiente información; es una etiqueta en un reloj de pared en algún lugar, y cuál pared depende de la zona que proporciones.
  • La verificación final de inmutabilidad mostró que now no cambió después de plusDays(7).withHour(0).withMinute(0). Esa garantía se mantiene en cada operación, cada cadena, cada método auxiliar — no hay forma de mutar un LocalDateTime. Pásalo libremente, compártelo entre hilos, guárdalo en un Map.

Qué sigue

LocalDateTime es el último de los tres tipos "Local" — sin zona, sin pretender ser un momento. El siguiente capítulo, Java ZonedDateTime, agrega la zona explícitamente: un LocalDateTime más un ZoneId más el desplazamiento resuelto para esa hora local en esa zona, que juntos fijan un momento real en la línea de tiempo global.

Práctica

Práctica
Dos desarrolladores — uno en Nueva York, otro en Berlín — ambos construyen el valor `LocalDateTime.of(2025, 11, 4, 14, 0)`. ¿Son el mismo momento en el tiempo?
Dos desarrolladores — uno en Nueva York, otro en Berlín — ambos construyen el valor `LocalDateTime.of(2025, 11, 4, 14, 0)`. ¿Son el mismo momento en el tiempo?
Was this page helpful?