Wie konvertiere ich ZonedDateTime in Date?

Lesezeit: 6 Minuten

Benutzer-Avatar
Milen Kowatschow

Ich versuche, eine serverunabhängige Datumszeit in meiner Datenbank festzulegen, und ich glaube, dass die beste Vorgehensweise darin besteht, eine UTC-DateTime festzulegen. Mein DB-Server ist Cassandra und der DB-Treiber für Java versteht nur den Date-Typ.

Angenommen, ich verwende in meinem Code die neue Java 8 ZonedDateTime, um die UTC jetzt abzurufen (ZonedDateTime.now(ZoneOffset.UTC)), wie kann ich diese ZonedDateTime-Instanz in die “alte” Date-Klasse konvertieren?

  • Es gibt zwei in Java eingebaute “Date”-Klassen, java.util.Date und java.sql.Date.

    – Basilikum Bourque

    28. August 2015 um 17:02 Uhr


Sie können ZonedDateTime in einen Instant umwandeln, den Sie direkt mit Date verwenden können.

Date.from(java.time.ZonedDateTime.now().toInstant());

  • Nein, es wird das aktuelle Datum in Ihrem Zonensystemstandard sein.

    Benutzer3460409

    28. August 2015 um 15:53 ​​Uhr

  • @MilenKovachev Ihre Frage macht keinen Sinn – ein Datum hat keine Zeitzone – es stellt nur einen Zeitpunkt dar.

    – Assylias

    28. August 2015 um 16:29 Uhr

  • @assylias Eigentlich macht deine Aussage keinen Sinn. Das Format, in dem die Uhrzeit gespeichert wird, impliziert eine Zeitzone. Das Datum basiert auf UTC, leider macht Java einige dumme Dinge und behandelt es nicht als solches, und betrachtet obendrein die Zeit als lokale TZ statt als UTC. Die Art und Weise, wie Data, LocalDateTime, ZonedDateTime Daten speichern, impliziert eine Zeitzone. Die Art und Weise, wie sie verwendet werden, ist, als ob TZs nicht existieren würden, was einfach falsch ist. java.util.Date hat implizit eine TZ des JVM-Standards. Die Tatsache, dass die Leute es als etwas anderes behandeln (einschließlich der Java-Dokumentation dafür!), Ist nur schlechte Leute.

    – David

    23. Oktober 2015 um 17:28 Uhr


  • @ David äh nein – a Date ist eine Anzahl von Millisekunden seit der Epoche – ist also auf UTC bezogen. Wenn du drucken it, wird die Standardzeitzone verwendet, aber die Date-Klasse hat keine Kenntnis von der Zeitzone des Benutzers … Siehe zum Beispiel den Abschnitt “Zuordnung” in docs.oracle.com/javase/tutorial/datetime/iso/legacy.html Und LocalDateTime ist explizit ohne Bezug auf eine Zeitzone – was als verwirrend angesehen werden kann …

    – Assylias

    23. Oktober 2015 um 20:20 Uhr

  • Ihre Antwort beinhaltet unnötigerweise ZonedDateTime. Das java.time.Instant Klasse ist ein direkter Ersatz für java.util.Datedie beide jedoch einen Moment in UTC darstellen Instant verwendet eine feinere Auflösung von Nanosekunden anstelle von Millisekunden. Date.from( Instant.now() ) sollte deine Lösung gewesen sein. Oder einfach nur new Date() was den gleichen Effekt hat und den aktuellen Moment in UTC erfasst.

    – Basilikum Bourque

    16. Juli 2018 um 22:02 Uhr


Benutzer-Avatar
Basil Bourque

tl;dr

java.util.Date.from(  // Transfer the moment in UTC, truncating any microseconds or nanoseconds to milliseconds.
    Instant.now() ;   // Capture current moment in UTC, with resolution as fine as nanoseconds.
)

Obwohl der obige Code keinen Sinn hatte. Beide java.util.Date und Instant repräsentieren einen Moment in UTC, immer in UTC. Der obige Code hat die gleiche Wirkung wie:

new java.util.Date()  // Capture current moment in UTC.

Kein Vorteil hier zu verwenden ZonedDateTime. Wenn Sie bereits eine haben ZonedDateTimepassen Sie sie an UTC an, indem Sie a extrahieren Instant.

java.util.Date.from(             // Truncates any micros/nanos.
    myZonedDateTime.toInstant()  // Adjust to UTC. Same moment, same point on the timeline, different wall-clock time.
)

Andere Antwort Richtig

Die Antwort von ssoltanid befasst sich korrekt mit Ihrer spezifischen Frage, wie ein java.time-Objekt der neuen Schule konvertiert wird (ZonedDateTime) zu einer alten Schule java.util.Date Objekt. Extrahieren Sie die Instant aus ZonedDateTime und übergeben an java.util.Date.from().

Datenverlust

Beachten Sie, dass Sie werden Datenverlust erleidenwie Instant Spuren Nanosekunden seit Epoche während java.util.Date Spuren Millisekunden seit Epoche.

Diagramm, das Auflösungen von Millisekunden, Mikrosekunden und Nanosekunden vergleicht

Ihre Frage und Kommentare werfen andere Probleme auf.

Halten Sie Server in UTC

Das Host-Betriebssystem Ihrer Server sollte als Best Practice im Allgemeinen auf UTC eingestellt sein. Die JVM übernimmt diese Host-Betriebssystemeinstellung als Standardzeitzone in den mir bekannten Java-Implementierungen.

Zeitzone angeben

Sie sollten sich jedoch niemals auf die aktuelle Standardzeitzone der JVM verlassen. Anstatt die Hosteinstellung zu übernehmen, kann ein beim Starten einer JVM übergebenes Flag eine andere Zeitzone festlegen. Schlimmer noch: Jeder Code in jedem Thread jeder App kann jederzeit einen Anruf tätigen java.util.TimeZone::setDefault um diese Voreinstellung zur Laufzeit zu ändern!

Kassandra Timestamp Typ

Irgendein anständige Datenbank und der Treiber sollte automatisch die Anpassung einer übergebenen Datum-Uhrzeit an UTC für die Speicherung vornehmen. Ich verwende Cassandra nicht, aber es scheint eine rudimentäre Unterstützung für Datum und Uhrzeit zu haben. Die Dokumentation sagt es Timestamp Typ ist eine Anzahl von Millisekunden aus derselben Epoche (erster Moment von 1970 in UTC).

ISO 8601

Darüber hinaus akzeptiert Cassandra Zeichenfolgeneingaben in der ISO 8601 Standardformate. Glücklicherweise verwendet java.time ISO 8601-Formate als Standard zum Analysieren/Generieren von Zeichenfolgen. Das Instant Klasse’ toString Umsetzung wird gut tun.

Präzision: Millisekunde vs. Nanosekunde

Aber zuerst müssen wir die Nanosekunden-Präzision von ZonedDateTime auf Millisekunden reduzieren. Eine Möglichkeit besteht darin, mithilfe von Millisekunden einen neuen Instant zu erstellen. Glücklicherweise verfügt java.time über einige praktische Methoden zum Konvertieren von und in Millisekunden.

Beispielcode

Hier ist ein Beispielcode in Java 8 Update 60.

ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
…
Instant instant = zdt.toInstant();
Instant instantTruncatedToMilliseconds = Instant.ofEpochMilli( instant.toEpochMilli() );
String fodderForCassandra = instantTruncatedToMilliseconds.toString();  // Example: 2015-08-18T06:36:40.321Z

Oder danach Cassandra Java-Treiber dockönnen Sie passieren a java.util.Date Beispiel (nicht zu verwechseln mit java.sqlDate). Daraus könnte man also ein JuDate machen instantTruncatedToMilliseconds im Code oben.

java.util.Date dateForCassandra = java.util.Date.from( instantTruncatedToMilliseconds );

Wenn Sie dies oft tun, könnten Sie einen Einzeiler erstellen.

java.util.Date dateForCassandra = java.util.Date.from( zdt.toInstant() );

Aber es wäre schöner, eine kleine Utility-Methode zu erstellen.

static public java.util.Date toJavaUtilDateFromZonedDateTime ( ZonedDateTime zdt ) {
    Instant instant = zdt.toInstant();
    // Data-loss, going from nanosecond resolution to milliseconds.
    java.util.Date utilDate = java.util.Date.from( instant ) ;
    return utilDate;
}

Beachten Sie den Unterschied in diesem ganzen Code als in der Frage. Der Code der Frage hat versucht, die Zeitzone der ZonedDateTime-Instanz an UTC anzupassen. Aber das ist nicht nötig. Konzeptionell:

ZonedDateTime = Instant + ZoneId

Wir extrahieren einfach den Instant-Teil, der bereits in UTC ist (im Grunde in UTC, lesen Sie das Klassendokument für genaue Details).


Tabelle der Datums- und Uhrzeittypen in Java, sowohl modern als auch veraltet


Um java.time

Das java.time Framework ist in Java 8 und höher integriert. Diese Klassen ersetzen die lästigen alten Erbe Datum-Zeit-Klassen wie z java.util.Date, Calendar& SimpleDateFormat.

Das Joda-Zeit Projekt, jetzt in Wartungsmodusrät zur Migration in die java.time Klassen.

Weitere Informationen finden Sie unter Oracle-Tutorial. Und durchsuchen Sie Stack Overflow nach vielen Beispielen und Erklärungen. Spezifikation ist JSR310.

Sie dürfen umtauschen java.time Objekte direkt mit Ihrer Datenbank. Verwenden ein JDBC-Treiber gemäß JDBC 4.2 oder später. Keine Notwendigkeit für Saiten, keine Notwendigkeit für java.sql.* Klassen.

Wo erhalte ich die java.time-Klassen?

  • Java SE 8, Java SE 9, JavaSE 10und später
    • Eingebaut.
    • Teil der Standard-Java-API mit einer gebündelten Implementierung.
    • Java 9 fügt einige kleinere Funktionen und Korrekturen hinzu.
  • Java SE 6 und Java SE 7
    • Ein Großteil der java.time-Funktionalität wurde in Java 6 & 7 zurückportiert ThreeTen-Backport.
  • Android
    • Spätere Versionen von Android-Bundle-Implementierungen der java.time Klassen.
    • Für frühere Android-Versionen (<26) ist die ThreeTenABP Projekt passt ThreeTen-Backport (oben erwähnt). Sehen Wie benutzt man ThreeTenABP….

Tabelle, welche java.time-Bibliothek mit welcher Java- oder Android-Version verwendet werden soll

Das ThreeTen-Extra project erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen zu java.time. Sie können hier einige nützliche Klassen finden, wie z Interval, YearWeek, YearQuarterund mehr.

  • Wunderbare Erklärung, gut detailliert. Ich danke dir sehr!

    – Gianmarco F.

    26. März 2019 um 8:50 Uhr

  • Danke @Basil Bourque für deine vollständige Antwort. Ist die Leistung schlecht oder beeinträchtigt, wenn die Liste oder die Paginierung zurückgegeben und diese Liste Element für Element durchlaufen wird, um von einer Zone (angenommen UTC) in eine andere Zone zu konvertieren?

    – Entwickler152

    28. Dezember 2021 um 19:50 Uhr

  • @devloper152 Fragst du nach der Leistung von java.time einen Moment nach Zeitzone anpassen? Versuch es selber. Ich nehme an, Sie werden es ziemlich schnell finden. Ich bezweifle, dass Sie finden werden java.time ein Flaschenhals sein.

    – Basilikum Bourque

    28. Dezember 2021 um 20:01 Uhr


  • Ich bin sehr zufrieden mit Ihrer Wiedergabe, die ich bereits ausprobiert habe. Eine andere Frage, Sir, was ist Ihre Meinung beim Konvertieren von einer Zone in eine andere in der Liste im Backend oder Frontend (Angular)?

    – Entwickler152

    28. Dezember 2021 um 20:12 Uhr

  • @devloper152 Ich verwende Angular nicht. Ich baue Web-Apps komplett in reinem Java mit Vaadin Flow.

    – Basilikum Bourque

    28. Dezember 2021 um 22:44 Uhr


Benutzer-Avatar
David Rawson

Wenn Sie die verwenden ThreeTen Backport für Android und kann das neuere nicht verwenden Date.from(Instant instant) (was mindestens API 26 erfordert) können Sie verwenden:

ZonedDateTime zdt = ZonedDateTime.now();
Date date = new Date(zdt.toInstant().toEpochMilli());

oder:

Date date = DateTimeUtils.toDate(zdt.toInstant());

Bitte lesen Sie auch die Ratschläge in Basil Bourques Antwort

  • Der ThreeTen Backport (und ThreeTenABP) beinhalten a DateTimeUtils Klasse mit den Konvertierungsmethoden, also würde ich Date date = DateTimeUtils.toDate(zdt.toInstant());` verwenden. Es ist nicht so niedrig.

    – Ole VV

    17. Juni 2018 um 12:37 Uhr

Hier ist ein Beispiel für die Konvertierung der aktuellen Systemzeit in UTC. Dazu muss ZonedDateTime als String formatiert werden, und dann wird das String-Objekt mithilfe von java.text DateFormat in ein Datumsobjekt geparst.

    ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
    final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
    final DateFormat FORMATTER_YYYYMMDD_HH_MM_SS = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
    String dateStr = zdt.format(DATETIME_FORMATTER);

    Date utcDate = null;
    try {
        utcDate = FORMATTER_YYYYMMDD_HH_MM_SS.parse(dateStr);
    }catch (ParseException ex){
        ex.printStackTrace();
    }

Benutzer-Avatar
Avec

Die akzeptierte Antwort hat bei mir nicht funktioniert. Das zurückgegebene Datum ist immer das lokale Datum und nicht das Datum für die ursprüngliche Zeitzone. Ich lebe in UTC+2.

//This did not work for me
Date.from(java.time.ZonedDateTime.now().toInstant()); 

Ich habe mir zwei alternative Möglichkeiten ausgedacht, um das korrekte Datum von einer ZonedDateTime zu erhalten.

Angenommen, Sie haben diese ZonedDateTime für Hawaii

LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneId.of("US/Hawaii"); // UTC-10

oder für UTC, wie ursprünglich gefragt

Instant zulu = Instant.now(); // GMT, UTC+0
ZonedDateTime zdt = zulu.atZone(ZoneId.of("UTC"));

Variante 1

Wir können java.sql.Timestamp verwenden. Es ist einfach, aber es wird wahrscheinlich auch Ihre Programmierintegrität beeinträchtigen

Date date1 = Timestamp.valueOf(zdt.toLocalDateTime());

Variante 2

Wir erstellen das Datum aus Millis (beantwortet hier vorhin). Beachten Sie, dass der lokale ZoneOffset ein Muss ist.

ZoneOffset localOffset = ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now());
long zonedMillis = 1000L * zdt.toLocalDateTime().toEpochSecond(localOffset) + zdt.toLocalDateTime().getNano() / 1000000L;
Date date2 = new Date(zonedMillis);

Benutzer-Avatar
dwe

Für eine Docker-Anwendung wie beehuang kommentiert, sollten Sie Ihre Zeitzone festlegen.

Alternativ können Sie verwenden withZoneSameLocal. Zum Beispiel:

01.07.2014T00:00+02:00[GMT+02:00] wird umgewandelt durch

Date.from(zonedDateTime.withZoneSameLocal(ZoneId.systemDefault()).toInstant())

zu Di 01.07. 00:00:00 MESZ 2014 und von

Date.from(zonedDateTime.toInstant())

zu Mo. 30. Juni 22:00:00 UTC 2014

Benutzer-Avatar
Basil Bourque

Sie können dies mit dem tun java.time Klassen, die in Java 8 und höher integriert sind.

ZonedDateTime temporal = ...
long epochSecond = temporal.getLong(INSTANT_SECONDS);
int nanoOfSecond = temporal.get(NANO_OF_SECOND);
Date date = new Date(epochSecond * 1000 + nanoOfSecond / 1000000);

  • Entschuldigung, woher kommen zeitliche und all diese Konstanten?

    – Milen Kovachev

    28. August 2015 um 15:30 Uhr

  • @MilenKovachev Eine ZonedDateTime ist eine Instanz von Temporal

    – Peter Lawrey

    28. August 2015 um 15:43 Uhr


  • Danke, aber Sie hätten erwähnen sollen, dass Sie Ihre Antwort bearbeitet haben, damit mein Kommentar nicht albern erscheint.

    – Milen Kovachev

    28. August 2015 um 15:44 Uhr

  • @MilenKovachev Sie können einen Kommentar löschen, aber es ist keine dumme Frage.

    – Peter Lawrey

    28. August 2015 um 15:45 Uhr

  • Ich kann nicht verstehen, warum diese Antwort abgelehnt wurde. Instants verfolgen Sekunden und Nanosekunden. Dates verfolgen Millisekunden. Diese Umrechnung von einem zum anderen ist richtig.

    – scottb

    29. August 2015 um 0:34 Uhr


1333230cookie-checkWie konvertiere ich ZonedDateTime in Date?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy