Konvertieren zwischen java.time.LocalDateTime und java.util.Date

484

Java 8 verfügt über eine völlig neue API für Datum und Uhrzeit. Eine der nützlichsten Klassen in dieser API ist das LocalDateTimeHalten eines zeitzonenunabhängigen Datums-Zeit-Werts.

Es gibt wahrscheinlich Millionen von Codezeilen, die die Legacy-Klasse java.util.Datefür diesen Zweck verwenden. Wenn Sie also alten und neuen Code miteinander verbinden, müssen Sie zwischen beiden konvertieren. Wie kann dies getan werden, da es anscheinend keine direkten Methoden gibt, um dies zu erreichen?

Knut Arne Vedaa
quelle

Antworten:

706

Kurze Antwort:

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

Erklärung: (basierend auf dieser Frage über LocalDate)

Stellt trotz seines Namens java.util.Dateeinen Moment auf der Zeitachse dar, kein "Datum". Die tatsächlich im Objekt gespeicherten Daten sind longMillisekunden seit 1970-01-01T00: 00Z (Mitternacht zu Beginn von 1970 GMT / UTC).

Die äquivalente Klasse zu java.util.Datein JSR-310 ist Instant, daher gibt es bequeme Methoden, um die Konvertierung hin und her bereitzustellen:

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

Eine java.util.DateInstanz hat kein Konzept der Zeitzone. Dies mag seltsam erscheinen , wenn Sie rufen toString()auf ein java.util.Date, weil die toStringrelativ zu einer Zeitzone ist. Diese Methode verwendet jedoch tatsächlich die Standardzeitzone von Java im laufenden Betrieb, um die Zeichenfolge bereitzustellen. Die Zeitzone ist nicht Teil des aktuellen Zustands von java.util.Date.

Ein Instantenthält auch keine Informationen über die Zeitzone. Um von einer Instantin eine lokale Datums- / Uhrzeitangabe zu konvertieren, muss daher eine Zeitzone angegeben werden. Dies kann die Standardzone sein - ZoneId.systemDefault()- oder eine Zeitzone, die Ihre Anwendung steuert, z. B. eine Zeitzone aus den Benutzereinstellungen. LocalDateTimeverfügt über eine praktische Factory-Methode, die sowohl die Sofort- als auch die Zeitzone berücksichtigt:

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

Umgekehrt wird die LocalDateTimeZeitzone durch Aufrufen der atZone(ZoneId)Methode angegeben. Das ZonedDateTimekann dann direkt in ein konvertiert werden Instant:

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

Beachten Sie, dass die Konvertierung von LocalDateTimenach ZonedDateTimezu unerwartetem Verhalten führen kann. Dies liegt daran, dass aufgrund der Sommerzeit nicht jede lokale Datums- und Uhrzeitangabe vorhanden ist. Im Herbst / Herbst gibt es eine Überlappung in der lokalen Zeitleiste, in der dieselbe lokale Datums- und Uhrzeit zweimal vorkommt. Im Frühjahr gibt es eine Lücke, in der eine Stunde verschwindet. Weitere Informationen atZone(ZoneId)zur Definition der Konvertierung finden Sie im Javadoc von .

Zusammenfassung: Wenn Sie eine Hin- und Rückfahrt java.util.Datezu a LocalDateTimeund zurück zu a machen java.util.Date, kann es aufgrund der Sommerzeit zu einem anderen Zeitpunkt kommen.

Zusätzliche Informationen: Es gibt einen weiteren Unterschied, der sich auf sehr alte Daten auswirkt. java.util.Dateverwendet einen Kalender, der sich am 15. Oktober 1582 ändert, wobei Daten davor den julianischen Kalender anstelle des gregorianischen Kalenders verwenden. Im Gegensatz dazu java.time.*wird das ISO-Kalendersystem (entspricht dem Gregorianischen) für alle Zeiten verwendet. In den meisten Anwendungsfällen ist das ISO-Kalendersystem genau das, was Sie möchten. Beim Vergleich der Daten vor dem Jahr 1582 können jedoch merkwürdige Auswirkungen auftreten.

JodaStephen
quelle
5
Vielen Dank für die klare Erklärung. Besonders für warum java.util.Dateenthält keine Zeitzone, aber drucken Sie es während toString(). Die offizielle Dokumentation der Veranstaltung sagt dies beim Posten nicht klar aus.
Kirsche
Warnung: LocalDateTime.ofInstant(date.toInstant()... verhält sich nicht so, wie man es naiv erwarten würde. Zum Beispiel new Date(1111-1900,11-1,11,0,0,0);wird 1111-11-17 23:53:28dieser Ansatz verwendet. Sehen Sie sich die Implementierung an, java.sql.Timestamp#toLocalDateTime()wenn Sie das Ergebnis 1111-11-11 00:00:00im vorherigen Beispiel benötigen .
Hund
2
Ich habe einen Abschnitt über sehr alte Daten (vor 1582) hinzugefügt. FWIW, Ihr vorgeschlagener Fix kann durchaus falsch sein, da 1111-11-11 in java.util.Date aufgrund der unterschiedlichen Kalendersysteme (der Unterschied von 6,5 Minuten) derselbe tatsächliche Tag in der Geschichte ist wie 1111-11-18 in java.time tritt in vielen Zeitzonen vor 1900 auf)
JodaStephen
2
Auch erwähnenswert, dass ein java.sql.Date#toInstantwirft UnsupportedOperationException. Also nicht toInstantin einem RowMapper verwenden java.sql.ResultSet#getDate.
LazerBass
132

Hier ist, was ich mir ausgedacht habe (und wie bei allen Datums- und Uhrzeit-Rätseln wird es wahrscheinlich aufgrund einer seltsamen Zeitzonen-Leapyear-Tageslicht-Anpassung widerlegt: D)

Rundauslösung: Date<<- >>LocalDateTime

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

Beispiel:

Gegeben:

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

(1) LocalDateTime<< Instant<< Date:

Erstellen Instantaus Date:

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

Erstellen Dateaus Instant(nicht erforderlich, aber zur Veranschaulichung):

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

Erstellen LocalDateTimeausInstant

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

(2) Date<< Instant<<LocalDateTime

Erstellen Instantaus LocalDateTime:

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

Erstellen Dateaus Instant:

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

Die Ausgabe ist:

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
Saint Hill
quelle
2
@scottb adressierst du das an mich oder die Person, die die Frage gestellt hat? Was meine Gedanken angeht , wäre es schön , wenn die Konvertierung explizit in der jdk 8-API angegeben wäre - das hätten sie zumindest tun können. In jedem Fall gibt es viele Bibliotheken, die neu berücksichtigt werden, um die neuen Java-8-Funktionen aufzunehmen, und es ist wichtig zu wissen, wie das geht.
Der Koordinator
4
@scottb Warum sollte der Fall eines Drittanbieters ungewöhnlich sein? Es ist verdammt üblich. Ein Beispiel: JDBC 4 und weniger (hoffentlich nicht 5).
Raman
5
Als allgemeine JSR-310-Regel ist es nicht erforderlich, mit epoch-millis zwischen Typen zu konvertieren. Es gibt bessere Alternativen mit Objekten, siehe meine vollständige Antwort unten. Die obige Antwort ist auch nur dann vollständig gültig, wenn ein Zonenversatz wie UTC verwendet wird. Einige Teile der Antwort funktionieren nicht für eine vollständige Zeitzone wie America / New_York.
JodaStephen
2
Statt Instant.ofEpochMilli(date.getTime())dodate.toInstant()
Ziege
1
@goat toInstant()sieht gut aus, außer es schlägt fehl java.sql.Date, arggggh! So ist es endlich einfacher zu bedienen Instant.ofEpochMilli(date.getTime()).
Vadipp
22

Viel bequemer, wenn Sie sicher sind, dass Sie eine Standardzeitzone benötigen:

Date d = java.sql.Timestamp.valueOf( myLocalDateTime );
Petrychenko
quelle
25
Sicher, es ist einfacher, aber ich mag es nicht, jdbc-bezogene Sachen mit einfacher Datumsbehandlung zu mischen, zumindest IMHO.
Enrico Giurin
9
Es tut mir leid, dies ist eine schreckliche Lösung für ein einfaches Problem. Es ist so, als würde man 5 so definieren, dass es der Anzahl der Ringe im olympischen Logo entspricht.
Madbreaks
7

Folgendes scheint zu funktionieren, wenn von der neuen API LocalDateTime in java.util.date konvertiert wird:

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

Die umgekehrte Umwandlung kann (hoffentlich) auf ähnliche Weise erreicht werden ...

ich hoffe es hilft...

schwindlig
quelle
5

Alles ist hier: http://blog.progs.be/542/date-to-java-time

Die Antwort mit "Round-Tripping" ist nicht genau: wenn Sie dies tun

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

Wenn Ihre Systemzeitzone nicht UTC / GMT ist, ändern Sie die Zeit!

Joe-Mojo
quelle
Nur wenn Sie dies im Herbst 1 Stunde im Jahr tun, wenn sich zwei Male von LocalDateTime überschneiden. Die Vorwärtsbewegung der Feder verursacht kein Problem. Meistens werden beide Richtungen richtig konvertiert.
David
4

Der schnellste Weg für LocalDateTime-> Dateist:

Date.from(ldt.toInstant(ZoneOffset.UTC))

Lukasz Czerwinski
quelle
3

Ich bin mir nicht sicher, ob dies der einfachste oder beste Weg ist oder ob es Fallstricke gibt, aber es funktioniert:

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();
}
Knut Arne Vedaa
quelle
3
Es gibt definitiv eine Falle von LocalDateTimebis Date. Bei Sommerzeitübergängen LocalDateTimekann a nicht vorhanden sein oder zweimal auftreten. Sie müssen herausfinden, was in jedem Fall passieren soll.
Jon Skeet
1
Übrigens, GregorianCalendargehört alte umständliche API, die neue java.timeAPI ersetzen soll
Vadzim
3

Wenn Sie auf Android sind und threetenbp verwenden , können Sie DateTimeUtilsstattdessen verwenden.

Ex:

Date date = DateTimeUtils.toDate(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

Sie können nicht verwenden, Date.fromda es nur auf API 26+ unterstützt wird

humazed
quelle