16 Stimmen

JPA TemporalType.Date liefert falsches Datum

Ich habe eine Klasse, die ein Datumsfeld hat, das ein "Gültig von"-Datum für einen Teil der Daten darstellt. Es ist wie folgt definiert:

@Temporal( TemporalType.DATE )
private Date validFrom;

Alles scheint gut zu funktionieren, bis zu dem Punkt, an dem ich das Datum aus der Datenbank ziehe und es anzeige. Wenn ich das Datum 18-Sep-2003 im Frontend auswähle und es dann speichere, wenn ich in der Datenbank nachsehe, wird das Datum auch gehalten (Datenbank ist MySQL 5.5.9, Spaltentyp ist DATE). Wenn ich jedoch eine Liste von Datensätzen aufrufe, ist das angezeigte Datum der 17. September 2003 - ein Tag früher.

Wenn ich ein frühes oder spätes Datum im Jahr wähle, wie den 26. März 2003 oder den 25. Dezember 2003, ist alles in Ordnung, so dass ich vermute, dass dies etwas mit der Sommerzeit zu tun hat, aber wo schleicht sich der Fehler ein? Da die Datenbank das korrekte Datum zu enthalten scheint, vermute ich, dass es daran liegt, dass JPA zurück in ein java.util.Date konvertiert - ist java.util.Date die beste Klasse, die man für ein Datum verwenden kann? Ich habe ein paar Beispiele gesehen, wo Menschen Kalender verwenden, aber das scheint ziemlich schweres Gewicht und ich bin nicht sicher, wie gut es mit einem JSF-basierten Frontend funktionieren wird.

29voto

Darrell Teague Punkte 3794

Es tut mir sehr leid, aber alle bisherigen Antworten sind generell falsch. Die Antwort ist recht einfach, erfordert aber, dass wir fünf Punkte trennen:

  1. DATE = java.sql.Date, ein Wrapper um java.util.Date, der die Anzahl der Millisekunden seit der Epoche in der UTC-Zeitzone angibt. Dies hat also das Jahr/Monat/Datum/Stunden/Minuten/Sekunden in einer festen GMT+0 (UTC) Zeitzone. Beachten Sie jedoch, dass java.sql.Date die Zeitkomponenten auf Null setzt!
  2. TIMESTAMP = java.sql.TimeStamp ist ein Komponenten-Wrapper um Date, der Sekundenbruchteile hinzufügt, um den SQL DATE-Typ-Standard zu unterstützen. Diese Klasse/Typ ist nicht relevant oder erforderlich für diese Frage, aber kurz gesagt, dies hat das Datum plus die Zeit.
  3. Die Datenbank speichert DATE-Objekte wie definiert (mit UTC als Offset von Java), aber Mai die Uhrzeit übersetzen, wenn sie in der Datenbank für eine andere Zeitzone konfiguriert ist. Die meisten Datenbanken verwenden standardmäßig die Zeitzone des lokalen Servers, was eine sehr schlechte Idee ist. Meine Damen und Herren ... Speichern Sie DATE-Objekte IMMER in UTC. Lesen Sie weiter...
  4. Die Zeit in der JVM und die Zeitzone müssen stimmen. Da das Date-Objekt UTC verwendet, wird ein Offset für Ihre Serverzeit berechnet? Berücksichtigen Sie dies bei der dringenden Empfehlung, die Serverzeit auf GMT+0 (UTC) zu setzen.
  5. Schließlich, wenn wir das DATE aus der Datenbank (mit JSF oder was auch immer) rendern wollen, ist es sollte so eingestellt werden, dass die Zeitzone GMT+0 ist, und wenn dies auch vom Server aus geschieht ... werden Ihre Daten und Zeiten IMMER konsistent, referenzierbar und all die guten Dinge sein. Alles, was übrig bleibt, ist die Zeit zu rendern und das ist, wo der User-Agent (für eine Web-Anwendung zum Beispiel) pourrait verwendet werden, um die GMT+0-Zeit in die "lokale" Zeitzone des Benutzers zu übersetzen.

Zusammenfassung: Verwenden Sie UTC (GMT+0) auf dem Server, in der Datenbank und in Ihren Java-Objekten.

DATE und TIMESTAMP unterscheiden sich aus Sicht der Datenbank nur dadurch, dass TIMESTAMP zusätzliche Sekundenbruchteile enthält. Beide verwenden GMT+0 (implizit). JodaTime ist ein bevorzugtes Kalender-Framework, um mit all dem umzugehen, behebt aber nicht die Probleme von nicht übereinstimmenden JVM- und Datenbank-Zeitzoneneinstellungen.

Wenn Anwendungsdesigns von der JVM bis zur DB aufgrund von Sommerzeit, Uhrenanpassungen und allen möglichen anderen regionalen Spielen, die mit den lokalen Uhren der Welt gespielt werden, nicht GMT verwenden, werden die Zeiten von Transaktionen und allem anderen für immer verzerrt, nicht referenziell, inkonsistent usw. sein.

Eine weitere gute Antwort zu den Datentypen: java.util.Date vs java.sql.Date

Beachten Sie auch, dass Java 8 Updates mit besserer Datums-/Zeitbehandlung hat (endlich), aber das behebt nicht, dass die Serveruhr, auf der die JVM läuft, in einer Zeitzone und die Datenbank in einer anderen ist. An diesem Punkt findet immer eine Übersetzung statt. In jedem großen (intelligenten) Client, mit dem ich arbeite, sind die Zeitzonen der Datenbank und des JVM-Servers aus genau diesem Grund auf UTC eingestellt, auch wenn ihre Operationen größtenteils in einer anderen Zeitzone stattfinden.

5voto

wobblycogs Punkte 4013

Nach langem Experimentieren und Suchen bin ich ziemlich sicher, dass ich die Ursache des Problems gefunden habe. Das Datum wird in einem java.util.Date gespeichert, das mit dem ganzen Gepäck der Zeit und einer Zeitzone kommt. Es scheint, dass JPA das Datum 18 Sep 2003 aus der Datenbank liest und dann das Datum wie folgt auffüllt: "Thu Sep 18 00:00:00 BST 2003" - beachten Sie, dass die Zeitzone auf BST gesetzt wurde, wahrscheinlich weil sie nicht explizit von der Datenbank festgelegt wurde. Wie auch immer, es ist notwendig, die Ausgabe in der JSF-Seite zu formatieren, wenn Sie nur das Datum wie dieses sehen wollen:

<h:outputText value="#{t.validFrom}">
    <f:convertDateTime pattern="dd MMM yyyy"/>
</h:outputText>

Dabei wird jedoch davon ausgegangen, dass die Zeitzone die derzeit auf dem Rechner geltende ist. In meinem Fall ist die Zeitzone derzeit GMT (weil Winter ist), so dass, wenn das Datum "Thu Sep 18 00:00:00 BST 2003" angezeigt wird, es in GMT umgewandelt wird, indem eine Stunde abgezogen wird, so dass die Anzeige 17 Sep 2003 anzeigt.

2voto

Matt Handy Punkte 29671

Ich hatte das gleiche Problem. Ich kenne den Grund nicht, aber meine Abhilfe war die folgende:

In der Datenbank habe ich den Spaltentyp von DATE à DATETIME .

In der Entitätsklasse habe ich die @Temporal Anmerkung, behielt aber den Datentyp Date :

@Temporal(TemporalType.TIMESTAMP)
private Date myDate;

2voto

Die Verwendung von DATETIME anstelle von date führt jedoch zu einem Unterschied von einer Stunde (oder mehr, je nach Zeitzone), den Sie ignorieren können, wenn Sie ein Datum, aber keinen Zeitwert verarbeiten. Bei mir waren die Daten aus der Mysql-Datenbank der richtige Wert, aber der Unterschied trat auf, als ich f:convertDateTime ohne timeZone-Parameter verwendete, was dazu führte, dass standardmäßig GMT verwendet wurde!

<h:outputText value="#{test.dt}">
  <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss" timeZone="CET"/>
</h:outputText>

funktioniert gut, aber ich denke, das wird nicht mehr funktionieren, wenn wir auf MESZ umstellen ....

1voto

Revoman Punkte 214

Ich hatte das gleiche Problem, aber ich verwendete JSF 2 als Frontend. Wenn Sie JSF-Komponenten verwenden, schauen Sie sich diese andere Stackoverflow-Diskussion an und sehen Sie, dass JSF 2 sich nicht an die erwarteten TimeZone-Regeln hält. Die Designer haben es so implementiert, dass es immer GMT verwendet. In meiner Situation verursacht dies meine Termine um 5 oder 6 Stunden in der Datenbank, aber korrekt angezeigt werden.

JSF convertDateTime rendert den Vortag

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X