Ich benutze die Zeitzone Amerika / New York. Im Herbst "fallen" wir eine Stunde zurück - und "gewinnen" effektiv eine Stunde um 2 Uhr morgens. Am Übergangspunkt geschieht Folgendes:
Es ist 01:59:00 -04: 00 Uhr
und 1 Minute später wird es:
01:00:00 -05: 00 Uhr
Wenn Sie also einfach "1:30 Uhr" sagen, ist es nicht eindeutig, ob Sie sich auf das erste Mal beziehen, wenn 1:30 herumrollt, oder auf das zweite Mal. Ich versuche, Planungsdaten in einer MySQL-Datenbank zu speichern, und kann nicht bestimmen, wie die Zeiten richtig gespeichert werden sollen.
Hier ist das Problem:
"2009-11-01 00:30:00" wird intern als 2009-11-01 00:30:00 -04: 00
gespeichert. "2009-11-01 01:30:00" wird intern als gespeichert 2009-11-01 01:30:00 -05: 00
Das ist in Ordnung und ziemlich zu erwarten. Aber wie speichere ich etwas bis 01:30:00 -04: 00 ? Die Dokumentation enthält keine Unterstützung für die Angabe des Offsets. Wenn ich also versucht habe, den Offset anzugeben, wurde dieser ordnungsgemäß ignoriert.
Die einzigen Lösungen, an die ich gedacht habe, bestehen darin, den Server auf eine Zeitzone einzustellen, in der keine Sommerzeit verwendet wird, und die erforderlichen Transformationen in meinen Skripten durchzuführen (ich verwende dafür PHP). Aber das scheint nicht notwendig zu sein.
Vielen Dank für Anregungen.
Antworten:
Die Datumstypen von MySQL sind offen gesagt fehlerhaft und können nicht alle Zeiten korrekt gespeichert werden, es sei denn, Ihr System ist auf eine konstante Offset-Zeitzone wie UTC oder GMT-5 eingestellt. (Ich benutze MySQL 5.0.45)
Dies liegt daran, dass Sie während der Stunde vor Ende der Sommerzeit keine Zeit speichern können . Unabhängig davon, wie Sie Datumsangaben eingeben, behandelt jede Datumsfunktion diese Uhrzeiten so, als ob sie sich in der Stunde nach dem Wechsel befinden.
Die Zeitzone meines Systems ist
America/New_York
. Versuchen wir, 1257051600 (So, 01. November 2009, 06:00:00 +0100) zu speichern.Hier wird die proprietäre INTERVAL-Syntax verwendet:
Auch
FROM_UNIXTIME()
wird nicht die genaue Zeit zurückgeben.Seltsamerweise speichert DATETIME immer noch (nur in Zeichenfolgenform!) Zeiten innerhalb der "verlorenen" Stunde, in der die Sommerzeit beginnt (z
2009-03-08 02:59:59
. B. ). Die Verwendung dieser Daten in einer MySQL-Funktion ist jedoch riskant:Das Mitnehmen: Wenn Sie jedes Mal im Jahr speichern und abrufen müssen , haben Sie einige unerwünschte Optionen:
Speichern Sie Daten als INTs (wie Aaron herausfand, ist TIMESTAMP nicht einmal zuverlässig)
Stellen Sie sich vor, der Typ DATETIME hat eine konstante Versatzzeitzone. Wenn Sie beispielsweise in sind
America/New_York
, konvertieren Sie Ihr Datum außerhalb von MySQL in GMT-5 und speichern Sie es dann als DATETIME (dies stellt sich als wesentlich heraus: siehe Aarons Antwort). Dann müssen Sie bei der Verwendung der Datums- / Zeitfunktionen von MySQL sehr vorsichtig sein, da einige davon ausgehen, dass Ihre Werte der Systemzeitzone entsprechen, andere (insbesondere Zeitarithmetikfunktionen) "zeitzonenunabhängig" sind (sie verhalten sich möglicherweise so, als wären die Zeiten UTC).Aaron und ich vermuten, dass die automatisch generierenden TIMESTAMP-Spalten ebenfalls fehlerhaft sind. Beide
2009-11-01 01:30 -0400
und2009-11-01 01:30 -0500
werden als mehrdeutig gespeichert2009-11-01 01:30
.quelle
select '2009-11-01 00:00:00' + INTERVAL 3600 SECOND;
das folgende'2009-11-01 01:00:00'
. UNIX_TIMESTAMP versucht dann einfach, dies im Kontext der Sitzungszeitzone in UTC umzuwandeln - es wird nicht versucht, das Hinzufügen im Kontext der Sommerzeitregeln dieser Zeitzone durchzuführen.America/New_York
, sehe ich keine Möglichkeit, 1257051600 zu speichern.Ich habe es für meine Zwecke herausgefunden. Ich werde zusammenfassen, was ich gelernt habe (Entschuldigung, diese Notizen sind ausführlich; sie sind für meine zukünftige Überweisung genauso wichtig wie alles andere).
Im Gegensatz zu dem, was ich in einem meiner vorherigen Kommentare gesagt habe, verhalten sich die Felder DATETIME und TIMESTAMP unterschiedlich. TIMESTAMP-Felder (wie in den Dokumenten angegeben) nehmen alles, was Sie senden, im Format "JJJJ-MM-TT hh: mm: ss" und konvertieren es von Ihrer aktuellen Zeitzone in die UTC-Zeit. Das Gegenteil geschieht transparent, wenn Sie die Daten abrufen. DATETIME-Felder führen diese Konvertierung nicht durch. Sie nehmen alles, was Sie ihnen senden, und speichern es direkt.
Weder die Feldtypen DATETIME noch TIMESTAMP können Daten in einer Zeitzone genau speichern, in der die Sommerzeit eingehalten wird . Wenn Sie "2009-11-01 01:30:00" speichern, können die Felder nicht unterscheiden, welche Version von 1:30 Uhr Sie möchten - die Version -04: 00 oder -05: 00.
Ok, wir müssen unsere Daten in einer Zeitzone ohne Sommerzeit (z. B. UTC) speichern. TIMESTAMP-Felder können diese Daten aus Gründen, die ich erläutern werde, nicht genau verarbeiten: Wenn Ihr System auf eine DST-Zeitzone eingestellt ist, ist das, was Sie in TIMESTAMP eingeben, möglicherweise nicht das, was Sie zurückbekommen. Selbst wenn Sie Daten senden, die Sie bereits in UTC konvertiert haben, werden die Daten in Ihrer lokalen Zeitzone übernommen und eine weitere Konvertierung in UTC durchgeführt. Diese von TIMESTAMP erzwungene Hin- und Rückfahrt von lokal zu UTC zurück zu lokal ist verlustbehaftet, wenn Ihre lokale Zeitzone die Sommerzeit einhält (seit "2009-11-01 01:30:00" zwei verschiedenen möglichen Zeiten zugeordnet sind).
Mit DATETIME können Sie Ihre Daten in jeder gewünschten Zeitzone speichern und sicher sein, dass Sie alles zurückerhalten, was Sie senden (Sie werden nicht in die verlustbehafteten Roundtrip-Konvertierungen gezwungen, die Ihnen TIMESTAMP-Felder auferlegen). Die Lösung besteht also darin, ein DATETIME-Feld zu verwenden und vor dem Speichern in das Feld von Ihrer Systemzeitzone in eine Nicht-DST-Zone zu konvertieren, in der Sie es speichern möchten (ich denke, UTC ist wahrscheinlich die beste Option). Auf diese Weise können Sie die Konvertierungslogik in Ihre Skriptsprache integrieren, sodass Sie das UTC-Äquivalent von "2009-11-01 01:30:00 -04: 00" oder "" 2009-11-01 01:30: 00 -05: 00 ".
Ein weiterer wichtiger Punkt ist, dass die mathematischen Funktionen von MySQL für Datum und Uhrzeit nicht richtig um die Sommerzeitgrenzen herum funktionieren, wenn Sie Ihre Daten in einer Sommerzeit-TZ speichern. Umso mehr Grund, in UTC zu sparen.
Kurz gesagt, ich mache jetzt Folgendes:
Beim Abrufen der Daten aus der Datenbank:
Interpretieren Sie die Daten aus der Datenbank explizit als UTC außerhalb von MySQL, um einen genauen Unix-Zeitstempel zu erhalten. Ich benutze dafür die Funktion strtotime () von PHP oder die DateTime-Klasse. Dies kann in MySQL mit den Funktionen CONVERT_TZ () oder UNIX_TIMESTAMP () von MySQL nicht zuverlässig durchgeführt werden, da CONVERT_TZ nur einen Wert 'JJJJ-MM-TT hh: mm: ss' ausgibt, der unter Mehrdeutigkeitsproblemen leidet, und UNIX_TIMESTAMP () seinen Wert annimmt Die Eingabe erfolgt in der Systemzeitzone, nicht in der Zeitzone, in der die Daten tatsächlich gespeichert wurden (UTC).
Beim Speichern der Daten in der Datenbank:
Konvertieren Sie Ihr Datum in die genaue UTC-Zeit, die Sie außerhalb von MySQL wünschen. Beispiel: Mit der DateTime-Klasse von PHP können Sie "2009-11-01 1:30:00 EST" deutlich von "2009-11-01 1:30:00 EDT" angeben, diese dann in UTC konvertieren und die richtige UTC-Zeit speichern zu Ihrem DATETIME-Feld.
Puh. Vielen Dank für alle Beiträge und Hilfe. Hoffentlich erspart dies jemand anderem später Kopfschmerzen.
Übrigens sehe ich das auf MySQL 5.0.22 und 5.0.27
quelle
Ich denke, der Link von micahwittman bietet die beste praktische Lösung für diese MySQL-Einschränkungen: Setzen Sie die Sitzungszeitzone auf UTC, wenn Sie eine Verbindung herstellen:
Dann senden Sie ihm einfach Unix-Zeitstempel und alles sollte in Ordnung sein.
quelle
Dieser Thread hat mich verrückt gemacht, da wir
TIMESTAMP
Spalten mitOn UPDATE CURRENT_TIMESTAMP
(dhrecordTimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
:) verwenden, um geänderte Datensätze und ETL in einem Datawarehouse zu verfolgen.Falls sich jemand wundert,
TIMESTAMP
verhalten Sie sich in diesem Fall korrekt und Sie können zwischen den beiden ähnlichen Daten unterscheiden, indem Sie denTIMESTAMP
Zeitstempel in Unix konvertieren :quelle
Sie können wie folgt in UTC konvertieren:
Noch besser, speichern Sie die Daten als TIMESTAMP- Feld. Das wird immer in UTC gespeichert, und UTC weiß nichts über Sommer- / Winterzeit.
Sie können mit CONVERT_TZ von UTC in Ortszeit konvertieren :
Dabei ist '+00: 00' UTC, die Zeitzone from, und 'SYSTEM' die lokale Zeitzone des Betriebssystems, in dem MySQL ausgeführt wird.
quelle
MySQL löst dieses Problem von Natur aus mithilfe der Tabelle time_zone_name aus der MySQL-Datenbank. Verwenden Sie CONVERT_TZ während CRUD, um die Datums- und Uhrzeitangabe zu aktualisieren, ohne sich um die Sommerzeit kümmern zu müssen.
quelle
Ich habe daran gearbeitet, die Anzahl der Besuche von Seiten zu protokollieren und die Anzahl in der Grafik anzuzeigen (mithilfe des Flot jQuery-Plugins). Ich füllte die Tabelle mit Testdaten und alles sah gut aus, aber ich bemerkte, dass am Ende des Diagramms die Punkte laut Beschriftungen auf der x-Achse einen Tag frei waren. Nach der Prüfung stellte ich fest, dass die Anzahl der Aufrufe für den Tag 2015-10-25 zweimal aus der Datenbank abgerufen und an Flot übergeben wurde, sodass jeder Tag nach diesem Datum um einen Tag nach rechts verschoben wurde.
Nachdem ich eine Weile nach einem Fehler in meinem Code gesucht hatte, wurde mir klar, dass an diesem Datum die Sommerzeit stattfindet. Dann kam ich zu dieser SO-Seite ...
... aber die vorgeschlagenen Lösungen waren ein Overkill für das, was ich brauchte, oder sie hatten andere Nachteile. Ich bin nicht sehr besorgt darüber, nicht zwischen mehrdeutigen Zeitstempeln unterscheiden zu können. Ich muss nur die Aufzeichnungen pro Tag zählen und anzeigen.
Zuerst rufe ich den Datumsbereich ab:
Dann rufe ich in einer for-Schleife, beginnend mit
min_date
, endend mitmax_date
Schritt eines Tages (60*60*24
), die Zählungen ab:Meine endgültige und schnelle Lösung für mein Problem war folgende:
Ich starre also nicht am Anfang des Tages, sondern zwei Stunden später auf die Schleife . Der Tag ist immer noch derselbe und ich rufe immer noch die richtigen Zählwerte ab, da ich die Datenbank explizit zwischen 00:00:00 und 23:59:59 des Tages nach Datensätzen frage, unabhängig von der tatsächlichen Zeit des Zeitstempels. Und wenn die Zeit um eine Stunde springt, bin ich immer noch am richtigen Tag.
Hinweis: Ich weiß, dass dies ein 5 Jahre alter Thread ist, und ich weiß, dass dies keine Antwort auf die Frage des OP ist, aber es könnte Leuten wie mir helfen, die auf diese Seite gestoßen sind, nach einer Lösung für das von mir beschriebene Problem zu suchen.
quelle
"SELECT CAST(created_timestamp AS date) day,COUNT(*) WHERE item_id=:item_id AND (created_timestamp BETWEEN '".date("Y-m-d 00:00:00", $min_date_timestamp)."' AND '".date("Y-m-d 23:59:59", $max_date_timestamp)."') GROUP BY day ORDER BY day";