Conversione tra java.time.LocalDateTime e java.util.Date

Java 8 ha un’API completamente nuova per data e ora. Una delle classi più utili in questa API è LocalDateTime , per mantenere un valore data-con-tempo indipendente dal fuso orario.

Ci sono probabilmente milioni di righe di codice che usano la class legacy java.util.Date per questo scopo. Come tale, quando si interfacciano vecchio e nuovo codice ci sarà bisogno di convertire tra i due. Poiché non sembra esserci alcun metodo diretto per realizzare questo, come può essere fatto?

Risposta breve:

 Date in = new Date(); LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); 

Spiegazione: (basato su questa domanda su LocalDate )

Nonostante il suo nome, java.util.Date rappresenta un istante sulla linea del tempo, non una “data”. I dati effettivi memorizzati all’interno dell’object sono un long conteggio di millisecondi dal 1970-01-01T00: 00Z (mezzanotte all’inizio del 1970 GMT / UTC).

La class equivalente a java.util.Date in JSR-310 è Instant , quindi ci sono metodi convenienti per fornire la conversione avanti e indietro:

 Date input = new Date(); Instant instant = input.toInstant(); Date output = Date.from(instant); 

Un’istanza java.util.Date non ha alcun concetto di fuso orario. Ciò potrebbe sembrare strano se si chiama toString() su un java.util.Date , perché toString è relativo a un fuso orario. Tuttavia, quel metodo utilizza in tempo reale il fuso orario predefinito di Java per fornire la stringa. Il fuso orario non fa parte dello stato attuale di java.util.Date .

Anche Instant non contiene alcuna informazione sul fuso orario. Pertanto, per convertire da Instant a data-ora locale è necessario specificare un fuso orario. Questa potrebbe essere la zona predefinita – ZoneId.systemDefault() – oppure potrebbe essere un fuso orario controllato dall’applicazione, ad esempio un fuso orario dalle preferenze dell’utente. LocalDateTime ha un metodo factory conveniente che prende sia l’istante che il fuso orario:

 Date in = new Date(); LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); 

Al contrario, LocalDateTime viene specificato il fuso orario chiamando il atZone(ZoneId) . Il ZonedDateTime può quindi essere convertito direttamente in un Instant :

 LocalDateTime ldt = ... ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault()); Date output = Date.from(zdt.toInstant()); 

Tieni presente che la conversione da LocalDateTime a ZonedDateTime ha il potenziale per introdurre comportamenti imprevisti. Questo perché non tutte le date locali sono disponibili a causa dell’ora legale. In autunno / autunno, si verifica una sovrapposizione nella linea temporale locale in cui la stessa data-ora locale si verifica due volte. In spring, c’è un vuoto, dove scompare un’ora. Vedere il Javadoc di atZone(ZoneId) per ulteriori informazioni sulla definizione della conversione.

Riepilogo, se si LocalDateTime round-trip di un java.util.Date a un LocalDateTime e si ritorna a un java.util.Date si potrebbe finire con un istante diverso a causa dell’ora legale.

Ulteriori informazioni: C’è un’altra differenza che interesserà le date molto vecchie. java.util.Date utilizza un calendario che ha probabilità del 15 ottobre 1582, con date precedenti che utilizzano il calendario giuliano invece di quello gregoriano. Al contrario, java.time.* Utilizza il sistema di calendario ISO (equivalente al gregoriano) per tutto il tempo. Nella maggior parte dei casi d’uso, il sistema di calendario ISO è quello che vuoi, ma potresti notare effetti strani quando confronti le date precedenti all’anno 1582.

Ecco cosa mi è venuto in mente (e come tutti gli enigmi di Date Time probabilmente sarà smentito in base a qualche strana regolazione del fuso orario – leapyear-daylight: D)

Round-trip: Date << - >> LocalDateTime

Data: 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); 

Esempio:

Dato:

 Date date = new Date(); System.out.println(date + " long: " + date.getTime()); 

(1) LocalDateTime << Instant << Date :

Crea Instant dalla Date :

 Instant instant = Instant.ofEpochMilli(date.getTime()); System.out.println("Instant from Date:\n" + instant); 

Crea Date da Instant (non necessario, ma per illustrazione):

 date = Date.from(instant); System.out.println("Date from Instant:\n" + date + " long: " + date.getTime()); 

Crea LocalDateTime da Instant

 LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC); System.out.println("LocalDateTime from Instant:\n" + ldt); 

(2) Date << Instant << LocalDateTime

Crea Instant da LocalDateTime :

 instant = ldt.toInstant(ZoneOffset.UTC); System.out.println("Instant from LocalDateTime:\n" + instant); 

Crea Date da Instant :

 date = Date.from(instant); System.out.println("Date from Instant:\n" + date + " long: " + date.getTime()); 

L’output è:

 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 

Un modo molto più conveniente se sei sicuro di aver bisogno di un fuso orario predefinito:

 Date d = java.sql.Timestamp.valueOf( myLocalDateTime ); 

il seguente sembra funzionare durante la conversione da nuova API LocalDateTime in java.util.date:

 Date.from(ZonedDateTime.of({time as LocalDateTime}, ZoneId.systemDefault()).toInstant()); 

la conversione inversa può essere (si spera) realizzata in modo simile …

spero che sia d’aiuto…

Non sono sicuro se questo è il modo più semplice o migliore, o se ci sono delle insidie, ma funziona:

 static public LocalDateTime toLdt(Date date) { GregorianCalendar cal = new GregorianCalendar(); cal.setTime(date); ZonedDateTime zdt = cal.toZonedDateTime(); return zdt.toLocalDateTime(); } static public Date fromLdt(LocalDateTime ldt) { ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); GregorianCalendar cal = GregorianCalendar.from(zdt); return cal.getTime(); } 

Tutto è qui: http://blog.progs.be/542/date-to-java-time

La risposta con “round-tripping” non è esatta: quando lo fai

 LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC); 

se il fuso orario del sistema non è UTC / GMT, cambi il tempo!