Java8 java.util.Date-Konvertierung in java.time.ZonedDateTime

75

Ich erhalte die folgende Ausnahme bei dem Versuch , konvertieren java.util.Datezu java.time.LocalDate.

java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: 2014-08-19T05:28:16.768Z of type java.time.Instant

Der Code lautet wie folgt:

public static Date getNearestQuarterStartDate(Date calculateFromDate){

    int[] quaterStartMonths={1,4,7,10};     
    Date startDate=null;

    ZonedDateTime d=ZonedDateTime.from(calculateFromDate.toInstant());
    int frmDateMonth=d.getMonth().getValue();

Stimmt etwas nicht mit der Art und Weise, wie ich die ZonedDateTimeKlasse benutze ?

Gemäß Dokumentation sollte dies ein java.util.DateObjekt in konvertieren ZonedDateTime. Das obige Datumsformat ist Standarddatum?

Muss ich auf Joda zurückgreifen?

Wenn jemand einen Vorschlag machen könnte, wäre es großartig.

Ironluca
quelle

Antworten:

104

Um ein Instantin ein zu transformieren ZonedDateTime, ZonedDateTimebietet die Methode ZonedDateTime.ofInstant(Instant, ZoneId). Damit

Angenommen, Sie möchten eine ZonedDateTimein der Standardzeitzone, sollte Ihr Code sein

ZonedDateTime d = ZonedDateTime.ofInstant(calculateFromDate.toInstant(),
                                          ZoneId.systemDefault());
JB Nizet
quelle
Hoppla. Vielen Dank. Jetzt behoben.
JB Nizet
Was ist, wenn ich es in dieselbe Zeitzone konvertieren möchte? Ich glaube, das Datum ist immer UTC, also kann ich einfach ZoneId.UTC machen?
Didier A.
6
Ein Datum hat überhaupt keine Zeitzone. Es ist nur eine Anzahl von Millisecodes, die seit einem bestimmten Moment vergangen sind (1970-01-01T00: 00 UTC). Es gibt keine "gleiche" Zeitzone. Wenn sich die ZonedDateTime in der UTC-Zeitzone befinden soll, müssen Sie ZoneOffset.UTC übergeben.
JB Nizet
2
@DidierA. A Dateähnelt a Instant, die beide UNIX-Zeitstempel sind. Es ist auch üblich zu sagen, dass UNIX-Zeitstempel UTC-Zeitzonen sind.
Kevinarpe
38

Um eine ZonedDateTime von einem Datum zu erhalten, können Sie Folgendes verwenden:

calculateFromDate.toInstant().atZone(ZoneId.systemDefault())

Sie können die toLocalDateMethode dann aufrufen , wenn Sie ein LocalDate benötigen. Siehe auch: Konvertieren Sie java.util.Date in java.time.LocalDate

Assylien
quelle
Die obige Lösung funktioniert auch für mich und die Konvertierung in LocalDate war sehr nützlich. Eine Frage aus Entwurfssicht sollte sich ZonedDateTime.from () wie erwartet verhalten, solange es mit einer java.time.Instant versehen ist. In diesem Fall ist es nicht wie erwartet. Ist das eine Art Inkonsistenz?
Ironluca
3
Für ZonedDateTime.from () ist eine ZoneId erforderlich, für Instant jedoch keine. Daher löst ZonedDateTime.from (sofort) eine Ausnahme aus.
JodaStephen
7

Die Antwort von Assylias und die Antwort von JB Nizet sind beide richtig:

  1. Rufen Sie die neue Konvertierungsmethode auf, die der Legacy-Klasse hinzugefügt wurde java.util.Date::toInstant.
  2. Rufen Sie an Instant::atZoneund übergeben Sie a ZoneId, was zu a führt ZonedDateTime.

Geben Sie hier die Bildbeschreibung ein

Ihr Codebeispiel richtet sich jedoch an Quartale. Lesen Sie dazu weiter.

Viertel

Sie müssen Ihre Quartiere nicht selbst verwalten. Verwenden Sie eine bereits geschriebene und getestete Klasse.

org.threeten.extra.YearQuarter

Die java.time- Klassen werden durch das ThreeTen-Extra- Projekt erweitert. Unter den vielen praktischen Klassen, die in dieser Bibliothek angeboten werden, finden Sie Quarterund YearQuarter.

Holen Sie sich zuerst Ihre ZonedDateTime.

ZonedId z = ZoneID.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = myJavaUtilDate.toInstant().atZone( z ) ;

Bestimmen Sie das Jahresquartal für dieses bestimmte Datum.

YearQuarter yq = YearQuarter.from( zdt ) ;

Als nächstes brauchen wir das Startdatum dieses Quartals.

LocalDate quarterStart = yq.atDay( 1 ) ;

Obwohl ich dies nicht unbedingt empfehle, können Sie eine einzelne Codezeile verwenden, anstatt eine Methode zu implementieren.

LocalDate quarterStart =                    // Represent a date-only, without time-of-day and without time zone.
    YearQuarter                             // Represent a specific quarter using the ThreeTen-Extra class `org.threeten.extra.YearQuarter`. 
    .from(                                  // Given a moment, determine its year-quarter.
        myJavaUtilDate                      // Terrible legacy class `java.util.Date` represents a moment in UTC as a count of milliseconds since the epoch of 1970-01-01T00:00:00Z. Avoid using this class if at all possible.
        .toInstant()                        // New method on old class to convert from legacy to modern. `Instant` represents a moment in UTC as a count of nanoseconds since the epoch of 1970-01-01T00:00:00Z. 
        .atZone(                            // Adjust from UTC to the wall-clock time used by the people of a particular region (a time zone). Same moment, same point on the timeline, different wall-clock time.
            ZoneID.of( "Africa/Tunis" )     // Specify a time zone using proper `Continent/Region` format. Never use 2-4 letter pseudo-zone such as `PST` or `EST` or `IST`. 
        )                                   // Returns a `ZonedDateTime` object.
    )                                       // Returns a `YearQuarter` object.
    .atDay( 1 )                             // Returns a `LocalDate` object, the first day of the quarter. 
;

Übrigens, wenn Sie Ihre Nutzung java.util.Dateinsgesamt auslaufen lassen können , tun Sie dies. Es ist eine schreckliche Klasse, zusammen mit seinen Geschwistern wie Calendar. Verwenden Sie Datenur dort, wo Sie müssen, wenn Sie mit altem Code arbeiten, der noch nicht auf java.time aktualisiert wurde .


Über java.time

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

Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .

Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration zu den Klassen java.time .

Sie können java.time- Objekte direkt mit Ihrer Datenbank austauschen . Verwenden Sie einen JDBC-Treiber, der mit JDBC 4.2 oder höher kompatibel ist . Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für java.sql.*Klassen.

Woher bekomme ich die java.time-Klassen?

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

Basil Bourque
quelle
0

Die Antwort hat bei Java 10, bei dem util.Date in UTC gespeichert wurde, nicht funktioniert.

Date.toInstant () scheint das EpochMillis in die lokale Zeitzone des Servers zu konvertieren.

ZDT.ofInstant (Instant, ZoneId) und Instant.atZone (ZoneId) scheinen im Moment nur eine TZ zu markieren, aber es ist bereits durcheinander.

Ich konnte keinen Weg finden, um zu verhindern, dass Date.toInstant () mit der UTC-Zeit und der Systemzeitzone in Konflikt gerät.

Die einzige Möglichkeit, dies zu umgehen, bestand darin, die sql.Timestamp-Klasse durchzugehen:

new java.sql.Timestamp(date.getTime()).toLocalDateTime()
                                      .atZone(ZoneId.of("UTC"))
                                      .withZoneSameInstant(desiredTZ)
Der Architekt
quelle
Nein, es gibt keine Konvertierung mit Date::toInstant. Sowohl a java.util.Dateals auch a java.time.Instantsind per Definition in UTC, immer in UTC. Datestellt eine Anzahl von Millisekunden seit der Epoche von 1970-01-01T00: 00: 00Z dar, während Instantes sich auch um eine Zählung seit derselben Epoche handelt, jedoch mit einer feineren Auflösung von Nanosekunden.
Basil Bourque
LocalDateTimeist genau die falsche Klasse, die hier verwendet wird. Dieser Klasse fehlt absichtlich jedes Konzept der Zeitzone oder des Versatzes von UTC. Als solches ist es nicht in der Lage, einen Moment darzustellen, wie in seiner Klasse JavaDoc angegeben. Beim Konvertieren in a werden LocalDateTimewertvolle Informationen verworfen, ohne dass etwas gewonnen wird. Diese Antwort ist irreführend und falsch. Siehe die richtige Antwort von Assylias. Alles was Sie brauchen, um ein ZonedDateTimevon einem zu bekommen, Dateist : myJavaUtilDate.toInstant().atZone( desiredZoneIdGoesHere ).
Basil Bourque
Basil, ich stimme zu 100% Ihrer Erwartung zu, aber leider haben wir nicht gesehen, wie sich Java 10 mit einem UTC-Datum in einer PST-JVM verhält. Wir verwenden hier nur die LocalDateTime als Zwischenvariable, um die UTC-Zeit nicht durcheinander zu bringen, bevor wir eine TZ darauf markieren.
TheArchitect
Es gibt keinen Grund, jemals java.sql.Timestampwieder zu verwenden, jetzt ersetzt durch java.sql.OffsetDateTime- ab JDBC 4.2 können wir einige java.time- Typen direkt mit der Datenbank austauschen .
Basil Bourque
Ich schlage vor, dass Sie anstelle dieser Antwort Ihre eigene Frage veröffentlichen, in der das aufgetretene Problem zusammen mit dem Beispielcode beschrieben wird. Wir sollten das für Sie klären können.
Basil Bourque