2014-04-01 9 views
17

Ich spiele gerade mit dem neuen Paket java.time in Java 8. Ich habe eine ältere Datenbank, die mir java.util.Date gibt, die ich in Instant umwandle.Java 8 java.time: Hinzufügen von TemporalUnit in Instant vs LocalDateTime

Was ich versuche, ist ein Zeitraum hinzuzufügen, der auf einem anderen Datenbankflag basiert. Ich könnte Tage, Wochen, Monate oder Jahre hinzufügen. Ich möchte mich nicht darum kümmern müssen, was ich hinzufügen möchte, und ich würde gerne in der Zukunft weitere Optionen hinzufügen können.

Mein erster Gedanke war Instant.plus(), aber das gibt mir eine UnsupportedTemporalTypeException für Werte größer als einen Tag. Instant unterstützt anscheinend keine Operationen in großen Zeiteinheiten. Gut, was auch immer, LocalDateTime tut.

So gibt, dass mir diesen Code:

private Date adjustDate(Date myDate, TemporalUnit unit){ 
    Instant instant = myDate.toInstant(); 
    LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 
    dateTime = dateTime.plus(1, unit); 
    Instant updatedInstant = dateTime.atZone(ZoneId.systemDefault()).toInstant(); 
    return new Date(dueInstant.toEpochMilli()); 
} 

Nun, dies ist mein erstes Mal die neue Zeit-API, so dass ich kann hier etwas übersehen haben. Aber es scheint klobig zu mir, dass ich gehen muss:

Date --> Instant --> LocalDateTime --> do stuff--> Instant --> Date. 

Auch wenn ich nicht das Datum Teil verwenden hatte, würde ich immer noch denke, es ist ein wenig umständlich war. Meine Frage ist also, mache ich das völlig falsch und was ist der beste Weg dies zu tun?


bearbeiten: in den Kommentaren über die Diskussion zu erweitern.

Ich denke, ich habe jetzt eine bessere Idee darüber, wie LocalDateTime und Instant mit java.util.Date und java.sql.Timestamp spielen. Danke an alle.

Nun, eine praktische Überlegung. Nehmen wir an, ein Benutzer sendet mir ein Datum von einer beliebigen Zeitzone in der Welt. Sie senden mir 2014-04-16T13:00:00, die ich in eine LocalDateTime analysieren kann. Ich konvertiere das dann direkt in einen java.sql.Timestamp und behalte es in meiner Datenbank bei.

Nun, ohne etwas anderes zu tun, ziehe ich meine java.sql.timestamp aus meiner Datenbank, umwandeln zu LocalDateTime mit timestamp.toLocalDateTime(). Alles gut. Dann gebe ich diesen Wert mithilfe der ISO_DATE_TIME-Formatierung an meinen Benutzer zurück. Das Ergebnis ist 2014-04-16T09:00:00.

Ich nehme an, dass dieser Unterschied wegen einer Art von impliziter Konvertierung zu/von UTC ist. Ich denke, dass meine Standardzeitzone auf den Wert (EDT, UTC-4) angewendet wird, der erklären würde, warum die Zahl um 4 Stunden aus ist.

Neue Frage (n). Wo passiert hier die implizite Konvertierung von Ortszeit in UTC? Was ist der bessere Weg Zeitzonen zu erhalten? Sollte ich nicht direkt von Ortszeit als String (2014-04-16T13: 00: 00) zu LocalDateTime gehen? Sollte ich von den Benutzereingaben eine Zeitzone erwarten?

+3

Welchen Wert sollen Sie hier darstellen? Ein 'Instant' kennt * logischerweise * nichts über ein Kalendersystem - es ist nur ein Zeitpunkt -, daher ist es nicht sinnvoll, einen Monat hinzuzufügen. Sie sollten auch sorgfältig abwägen, ob Sie * wirklich * die Systemzeitzone verwenden möchten - möchten Sie je nach Einsatzort unterschiedliche Ergebnisse für die gleichen Werte erhalten? –

+0

@JonSkeet Wäre es angemessener, eine ZoneId dann willkürlich auszuwählen? Immer GMT oder so? Es scheint, als könnte das mit einer eigenen Reihe von Problemen kommen. Vielleicht ist das Problem, dass ich nicht weiß, wie ich meine java.util.date darstellen soll. Ich habe einen Zeitpunkt. Wenn bestimmte Bedingungen erfüllt sind, möchte ich diesen Punkt in Zukunft auf einen Monat (oder Tag, Jahr, was auch immer) ändern. – jacobhyphenated

+0

Die Verwendung von UTC kann die beste Option sein, aber Sie müssen wirklich darüber nachdenken, was Ihre Anforderungen sind. Was bedeutet es sogar, einen Zeitpunkt (ohne Zeitzone oder Kalender) um einen Monat zu ändern? –

Antwort

16

Ich werde weitermachen und eine Antwort auf meine endgültige Lösung und eine Art Zusammenfassung der sehr langen Kommentarkette posten.

zu starten, die gesamte Umwandlungskette von:

Date --> Instant --> LocalDateTime --> Do stuff --> Instant --> Date 

ist notwendig, die Zeitzoneninformationen und noch tun Operationen an einem Datum wie Objekt zu erhalten, die darin von einem Kalender und allen Kontextes bewusst ist.Andernfalls laufen wir Gefahr, implizit in die lokale Zeitzone zu konvertieren, und wenn wir versuchen, sie in ein für Menschen lesbares Datumsformat zu bringen, könnten sich die Zeiten deshalb geändert haben.

Zum Beispiel wird die toLocalDateTime()-Methode der Klasse java.sql.Timestamp implizit in die Standardzeitzone konvertiert. Dies war für meine Zwecke nicht wünschenswert, ist aber nicht unbedingt schlechtes Benehmen. Es ist jedoch wichtig, sich dessen bewusst zu sein. Dies ist das Problem bei der direkten Konvertierung von einem Legacy-Java-Datumsobjekt in ein LocalDateTime Objekt. Da Legacy-Objekte im Allgemeinen als UTC angenommen werden, verwendet die Konvertierung den lokalen Zeitzonen-Offset.

Jetzt sagen wir, unser Programm nimmt die Eingabe von 2014-04-16T13:00:00 und speichert in einer Datenbank als java.sql.Timestamp.

//Parse string into local date. LocalDateTime has no timezone component 
LocalDateTime time = LocalDateTime.parse("2014-04-16T13:00:00"); 

//Convert to Instant with no time zone offset 
Instant instant = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

//Easy conversion from Instant to the java.sql.Timestamp object 
Timestamp timestamp = Timestamp.from(instant); 

Jetzt nehmen wir einen Zeitstempel und eine gewisse Anzahl von Tagen, um es hinzuzufügen:

Timestamp timestamp = ... 

//Convert to LocalDateTime. Use no offset for timezone 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 

//Add time. In this case, add one day. 
time = time.plus(1, ChronoUnit.DAYS); 

//Convert back to instant, again, no time zone offset. 
Instant output = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

Timestamp savedTimestamp = Timestamp.from(output); 

Jetzt müssen nur wir als Menschen lesbaren String im Format von ISO_LOCAL_DATE_TIME ausgegeben wird.

Timestamp timestamp = .... 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 
String formatted = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(time); 
+6

ZoneOffset.ofHours (0) kann besser als ZoneOffset.UTC ausgedrückt werden. Möglicherweise ist es auch einfacher, mit "ZonedDateTime" zu arbeiten, da diese Klasse das gesamte Plus/Minus-Verhalten von LocalDateTime unterstützt, aber die Zeitzoneninformationen beibehält. – JodaStephen

+0

Für den spezifischen Anwendungsfall von Tagen benötigen Sie keine (Orts-/Zonen-) Datumszeit, da eine Dauer von Tagen direkt zu einem Zeitpunkt hinzugefügt werden kann: 'Timestamp.from (timestamp.toInstant(). Plus (Duration. ofDays (1))) ' –

+1

Das Hinzufügen von" Duration.ofDays "zu einem' Instant' berücksichtigt keine Sommerzeit oder ähnliches, also nicht empfohlen. Konvertiere stattdessen in eine 'ZonedDateTime'. –

Verwandte Themen