Conversión entre java.time.LocalDateTime y java.util.Date
Java 8 tiene una API completamente nueva para fecha y hora. Una de las clases más útiles de esta API es LocalDateTime
, para mantener un valor de fecha con hora independiente de la zona horaria.
Probablemente haya millones de líneas de código que utilizan la clase heredada java.util.Date
para este propósito. Como tal, al interconectar código antiguo y nuevo, será necesario realizar una conversión entre los dos. Como parece no haber métodos directos para lograr esto, ¿cómo se puede hacer?
Respuesta corta:
Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
Explicación: (basado en esta pregunta sobre LocalDate
)
A pesar de su nombre, java.util.Date
representa un instante en la línea de tiempo, no una "fecha". Los datos reales almacenados dentro del objeto son una long
cuenta de milisegundos desde 1970-01-01T00:00Z (medianoche al comienzo de las 1970 GMT/UTC).
La clase equivalente java.util.Date
en JSR-310 es Instant
, por lo que existen métodos convenientes para proporcionar la conversión de un lado a otro:
Date input = new Date();
Instant instant = input.toInstant();
Date output = Date.from(instant);
Una java.util.Date
instancia no tiene concepto de zona horaria. Esto puede parecer extraño si llama toString()
a java.util.Date
, porque toString
es relativo a una zona horaria. Sin embargo, ese método en realidad utiliza la zona horaria predeterminada de Java sobre la marcha para proporcionar la cadena. La zona horaria no forma parte del estado real de java.util.Date
.
An Instant
tampoco contiene ninguna información sobre la zona horaria. Por lo tanto, para convertir de una Instant
fecha y hora local a una fecha y hora, es necesario especificar una zona horaria. Esta podría ser la zona predeterminada, ZoneId.systemDefault()
o podría ser una zona horaria que controle su aplicación, como una zona horaria de las preferencias del usuario. LocalDateTime
tiene un método de fábrica conveniente que toma tanto el instante como la zona horaria:
Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
A la inversa, la LocalDateTime
zona horaria se especifica llamando al atZone(ZoneId)
método. Luego se ZonedDateTime
puede convertir directamente en Instant
:
LocalDateTime ldt = ...
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date output = Date.from(zdt.toInstant());
Tenga en cuenta que la conversión de LocalDateTime
a ZonedDateTime
tiene el potencial de introducir un comportamiento inesperado. Esto se debe a que no todas las fechas y horas locales existen debido al horario de verano. En otoño/otoño, hay una superposición en la línea de tiempo local donde la misma fecha y hora local ocurre dos veces. En primavera hay un intervalo en el que desaparece una hora. Consulte el Javadoc de atZone(ZoneId)
para obtener más información sobre la definición de lo que hará la conversión.
En resumen, si realiza un viaje de ida y vuelta java.util.Date
de a a LocalDateTime
y de regreso a a java.util.Date
, puede terminar con un instante diferente debido al horario de verano.
Información adicional: Hay otra diferencia que afectará a fechas muy antiguas. java.util.Date
utiliza un calendario que cambia el 15 de octubre de 1582, con fechas anteriores utilizando el calendario juliano en lugar del gregoriano. Por el contrario, java.time.*
utiliza el sistema de calendario ISO (equivalente al gregoriano) para todos los tiempos. En la mayoría de los casos de uso, lo que desea es el sistema de calendario ISO, pero es posible que vea efectos extraños al comparar fechas anteriores al año 1582.
Esto es lo que se me ocurrió (y como todos los enigmas de fecha y hora, probablemente será refutado en función de algún ajuste extraño de zona horaria, año bisiesto y luz diurna: D)
Ida y vuelta: Date
<<->>LocalDateTime
Dado:Date date = [some date]
(1) LocalDateTime
<< Instant
<<Date
Instant instant = Instant.ofEpochMilli(date.getTime());
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
(2) Date
<< Instant
<<LocalDateTime
Instant instant = ldt.toInstant(ZoneOffset.UTC);
Date date = Date.from(instant);
Ejemplo:
Dado:
Date date = new Date();
System.out.println(date + " long: " + date.getTime());
(1) LocalDateTime
<< Instant
<< Date
:
Crear Instant
desde Date
:
Instant instant = Instant.ofEpochMilli(date.getTime());
System.out.println("Instant from Date:\n" + instant);
Crear Date
desde Instant
(no es necesario, pero a modo de ilustración):
date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());
Crear LocalDateTime
desdeInstant
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
System.out.println("LocalDateTime from Instant:\n" + ldt);
(2) Date
<< Instant
<<LocalDateTime
Crear Instant
desde LocalDateTime
:
instant = ldt.toInstant(ZoneOffset.UTC);
System.out.println("Instant from LocalDateTime:\n" + instant);
Crear Date
desde Instant
:
date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());
La salida es:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574
Instant from Date:
2013-11-01T14:13:04.574Z
Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574
LocalDateTime from Instant:
2013-11-01T14:13:04.574
Instant from LocalDateTime:
2013-11-01T14:13:04.574Z
Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574
Una forma mucho más conveniente si está seguro de que necesita una zona horaria predeterminada:
Date d = java.sql.Timestamp.valueOf( myLocalDateTime );
La forma más rápida para LocalDateTime
-> Date
es:
Date.from(ldt.toInstant(ZoneOffset.UTC))