So speichern Sie eine Datums- / Uhrzeitangabe in MySQL mit Zeitzoneninformationen

74

Ich habe Tausende von Fotos, die in Tansania aufgenommen wurden, und ich möchte das Datum und die Uhrzeit der Aufnahme jedes Fotos in einer MySQL-Datenbank speichern. Der Server befindet sich jedoch in den USA und ich habe Probleme, wenn ich versuche, eine tansanische Datums- und Uhrzeitangabe zu speichern, die während der Sommerzeit (in den USA) innerhalb der "ungültigen" Stunde liegt. Tansania führt keine Sommerzeit durch, daher ist die Zeit eine tatsächlich gültige Zeit.

Zusätzliche Komplikationen sind, dass es Mitarbeiter aus vielen verschiedenen Zeitzonen gibt, die auf die in der Datenbank gespeicherten Datums- und Uhrzeitwerte zugreifen müssen. Ich möchte, dass sie immer als tansanische Zeit herauskommen und nicht in der lokalen Zeit, in der sich verschiedene Mitarbeiter befinden.

Ich zögere es, Sitzungszeiten festzulegen, da ich weiß, dass es Probleme geben wird, wenn jemand manchmal vergisst, eine Sitzungszeit festzulegen, und die Zeiten falsch angegeben werden. Und ich habe keine Berechtigung, etwas am Server zu ändern.

Ich habe gelesen: Best Practices für Sommerzeit und Zeitzone sowie MySQL-Datums- und Uhrzeitfelder und Sommerzeit - wie verweise ich auf die "zusätzliche" Stunde? und Speichern von datetime als UTC in PHP / MySQL

Aber keiner von ihnen scheint mein spezielles Problem anzusprechen. Ich bin kein SQL-Experte. Gibt es eine Möglichkeit, die Zeitzone beim Festlegen von DATETIMEs anzugeben? Ich habe keinen gesehen. Ansonsten sind Vorschläge zur Lösung dieses Problems sehr willkommen.

Bearbeiten: Hier ist ein Beispiel für das Problem, auf das ich stoße. Ich sende den Befehl:

INSERT INTO Images (CaptureEvent, SequenceNum, PathFilename, TimestampJPG) 
VALUES (122,1,"S2/B04/B04_R1/IMAG0148.JPG","2011-03-13 02:49:10")

Und ich bekomme den Fehler:

Error 1292: Incorrect datetime value: '2011-03-13 02:49:10' for column 'TimestampJPG'

Dieses Datum und diese Uhrzeit existieren in Tansania, jedoch nicht in den USA, wo sich die Datenbank befindet.

mkosmala
quelle
13
Sie sollten keine Zeitzoneninformationen in der Datenbank speichern wollen. Speichern Sie alle Datums- / Zeitdaten als UTC und nehmen Sie den Zeitzonenversatz immer auf der Anwendungsebene vor.
Marekful
3
@ MarcellFülöp: Ich sehe immer wieder Leute, die sagen "Speichern als UTC", aber ich verstehe nicht, was das bedeutet. Wie kann ich eine Datums- und Uhrzeitangabe "als UTC" speichern? Soweit ich das beurteilen kann, kann ich nur etwas in der Form JJJJ-MM-TT HH: MM: SS einfügen. Wo kann ich der Datenbank mitteilen, dass es sich um UTC handelt?
mkosmala
Das tust du nicht. MySQL verwendet standardmäßig die Systemzeitzone intern, es ist jedoch möglich, eine andere Zeitzone für den MySQL-Server global oder sogar pro Transaktion zu definieren. Wenn Sie ein Datum einfügen, ist es in MySQL nicht möglich, die Zeitzone zusammen mit der Datumszeichenfolge zu definieren. Ein Datum wie '2013-11-10 00:00' bezieht sich auf einen Zeitpunkt seit der Epoche. Sie speichern es und wissen, in welcher Zeitzone sich Ihr Server befindet. Wenn Sie es abrufen, können Sie die erforderlichen Anpassungen vornehmen, um dieses Datum von der Zeitzone des Servers in die des Kunden umzuwandeln.
Marekful

Antworten:

68

Du sagtest:

Ich möchte, dass sie immer als tansanische Zeit herauskommen und nicht in der lokalen Zeit, in der sich verschiedene Mitarbeiter befinden.

Wenn dies der Fall ist, sollten Sie UTC nicht verwenden. Sie müssen lediglich einen DATETIMETyp in MySQL anstelle eines TIMESTAMPTyps verwenden.

Aus der MySQL-Dokumentation :

MySQL konvertiert TIMESTAMPWerte aus der aktuellen Zeitzone zum Speichern in UTC und zum Abrufen von UTC zurück in die aktuelle Zeitzone. (Dies tritt bei anderen Typen wie z. B. nicht auf DATETIME.)

Wenn Sie bereits einen DATETIMETyp verwenden, müssen Sie ihn zunächst nicht auf die Ortszeit einstellen. Sie müssen sich weniger auf die Datenbank als vielmehr auf Ihren Anwendungscode konzentrieren - den Sie hier nicht angezeigt haben. Das Problem und die Lösung variieren je nach Sprache drastisch. Kennzeichnen Sie die Frage daher unbedingt mit der entsprechenden Sprache Ihres Anwendungscodes.

Matt Johnson-Pint
quelle
2
Ich benutze eine DATETIME. Die Datenbank gibt jedoch einen Fehler aus, wenn ich versuche, eine Zeit festzulegen, die in den USA nicht existiert (in Tansania jedoch). Ich habe meine Frage bearbeitet, um ein Beispiel für die Probleme zu geben, auf die ich stoße. Mein Anwendungscode befindet sich zufällig in Python, aber im Moment ist es wirklich nur ein Wrapper um SQL-Code. Ich mache gerne Transformationen in Python, bin mir aber nicht sicher, was sie sein sollen.
mkosmala
2
Könnten Sie dann bitte Ihren Python-Code anzeigen, damit wir sehen können, wie Sie die SQL-Abfrage erstellen? Möglicherweise haben Sie es in Python nur mit naiven oder bewussten Datenzeiten zu tun. Und sind Sie sicher, dass Ihre TimestampJPGSpalte ein DATETIMETyp im MySQL-Datenbankschema und kein TIMESTAMPTyp ist?
Matt Johnson-Pint
1
Arg. Du hast recht. Ich habe alle anderen TIMESTAMPs in der Datenbank in DATETIME geändert, aber irgendwie habe ich diese verpasst. Das löst das Problem. Vielen Dank.
mkosmala
18
Dies ist ein weiterer Bereich, in dem es Ihnen mit MySQL leid tut, nicht stattdessen Postgresql zu verwenden. ;)
poige
6
@poige Warum sagst du, dass Postgresql hier besser sein wird? Außerdem wird der Zeitstempel vor dem Speichern in UTC konvertiert.
Anshul
23

Keine der Antworten hier traf den Nagel auf den Kopf.

So speichern Sie eine Datums- / Uhrzeitangabe in MySQL mit Zeitzoneninformationen

Verwenden Sie zwei Spalten : DATETIME, und a VARCHAR, um die Zeitzoneninformationen zu speichern, die in verschiedenen Formen vorliegen können:

Eine Zeitzone oder ein Ort wie America/New_Yorkdie höchste Datentreue.

Eine Zeitzonenabkürzung wie PSTdie nächsthöhere Wiedergabetreue.

Ein Zeitversatz wie dies -2:00ist die kleinste Datenmenge in dieser Hinsicht.

Einige wichtige Punkte:

  • Vermeiden Sie dies, TIMESTAMPda es auf das Jahr 2038 beschränkt ist und MySQL es mit der Server-Zeitzone in Verbindung bringt, was wahrscheinlich unerwünscht ist.
  • Ein Zeitversatz sollte nicht naiv in einem INTFeld gespeichert werden , da es Versätze von einer halben Stunde und einer Viertelstunde gibt.

Wenn es für Ihren Anwendungsfall wichtig ist, dass MySQL diese Daten chronologisch vergleicht oder sortiert , DATETIMEliegt ein Problem vor:

'2009-11-10 11:00:00 -0500'ist vorher '2009-11-10 10:00:00 -0700'in Bezug auf "Augenblick", aber sie würden in umgekehrter Reihenfolge sortieren, wenn sie in a eingefügt werden DATETIME.

Sie können Ihre eigene Konvertierung in UTC durchführen. In dem obigen Beispiel würden Sie dann haben
'2009-11-10 16:00:00'und '2009-11-10 17:00:00'jeweils die würde Art richtig. Wenn Sie die Daten abrufen, verwenden Sie die Zeitzoneninformationen, um sie in ihre ursprüngliche Form zurückzusetzen.

Eine Empfehlung, die mir sehr gefällt, sind drei Spalten:

  • local_time DATETIME
  • utc_time DATETIME
  • time_zone VARCHAR(X)Dabei ist X für die Art der Daten geeignet, die Sie dort speichern. (Ich würde 64 Zeichen für Zeitzone / Ort wählen.)

Ein Vorteil des 3-Spalten-Ansatzes besteht darin, dass er explizit ist: Bei einer einzelnen DATETIMESpalte können Sie nicht auf einen Blick erkennen, ob sie vor dem Einfügen in UTC konvertiert wurde.


In Bezug auf die Abnahme der Genauigkeit durch Zeitzone / Abkürzung / Offset:

  • Wenn Sie den Benutzer haben Zeitzone / Lage wie America/Juneaukönnen Sie wissen genau , was die Wanduhr Zeit für sie in der Vergangenheit oder Zukunft (abgesehen von Änderungen an der Art und Weise Sommer gehandhabt wird an dieser Stelle) an jedem Punkt ist. Die Start- / Endpunkte der Sommerzeit und ob sie überhaupt verwendet werden, hängen vom Standort ab. Dies ist also der einzig zuverlässige Weg.
  • Wenn Sie eine Zeitzonenabkürzung wie MST (Mountain Standard Time) oder einen einfachen Versatz wie haben -0700, können Sie keine Wanduhrzeit in der Vergangenheit oder Zukunft vorhersagen. In den USA verwenden beispielsweise Colorado und Arizona beide MST, Arizona beobachtet jedoch keine Sommerzeit. Wenn der Benutzer sein Katzenfoto 14:00 -0700während der Wintermonate hochlädt , war er dann in Arizona oder Kalifornien? Wenn Sie genau sechs Monate zu diesem Datum hinzufügen würden, wäre es 14:00oder 13:00für den Benutzer?

Diese Dinge sind wichtig zu berücksichtigen, wenn Ihre Anwendung Zeit, Datum oder Zeitplanung als Kernfunktion hat.


Verweise:

Matt Mc
quelle
2
Ich sehe keinen Vorteil darin, die Ortszeit zu speichern. IMHO, der sauberste Weg, um den datetime-Wert selbst zu speichern, ist in UTC. Dies vermeidet Verwirrung beim Vergleich von Datumszeiten. Wenn es einen Grund gibt, zu wissen, was das zu einer bestimmten Ortszeit war, zeichnen Sie einen STANDORT auf. Die App-Logik kann diesen Ort dann anwenden, um die Ortszeit zu ermitteln (z. B. während der Arbeitszeit?), ABER die DateTime selbst in UTC zu halten. Zum Beispiel ist unsere schwedische Einrichtung in der schwedischen Zeitzone definiert. Wir haben bereits ein Feld (in einem Ereignisdatensatz), in dem "an welcher Einrichtung" steht. Sie müssen also nicht SEPARAT jedem Ereignisdatensatz eine Zeitzone hinzufügen.
ToolmakerSteve
2
Der Vorteil der Aufbewahrung von Daten in UTC wird deutlich, wenn jemand an einem Ort eine Veranstaltung für einen anderen Ort betritt. Wenn ein Techniker in New York ein Ereignis in Schweden meldet, ist die "Ortszeit" New York oder Schweden? Vermeiden Sie diese Verwirrung, indem Sie eine UTC-Zeit speichern. Der Standort des Technikers und der Standort der Einrichtung, die mit dem Ereignis verbunden sind, ermöglichen es, es wie gewünscht in jeder "Ortszeit" ohne Verwirrung anzuzeigen - einfach durch Angabe von POV. Oder Sie interessieren sich vielleicht dafür, wann es in IHRER Zeit passiert ist, was eine dritte "Ortszeit" sein könnte. Geben Sie "lokaler Versatz" dynamisch über die Position an.
ToolmakerSteve
3
@ToolmakerSteve Wenn Sie wieder meine Antwort lesen, werden Sie sehen , dass mein primärer Punkt ist , dass Sie speichern müssen timeund locationin separaten Spalten. Ich spreche auch über das Speichern der UTC-Zeit. Wenn Sie es vorziehen, nur die UTC-Zeit zu speichern, weil Ihre locations Plural und / oder in einer anderen Tabelle sind, großartig. Mein einziger Vorschlag ist dann, es der utc_timeKlarheit halber zu kennzeichnen.
Matt Mc
6

MySQL speichert DATETIME ohne Zeitzoneninformationen. Angenommen, Sie speichern '2019-01-01 20:00:00' in einem DATETIME-Feld. Wenn Sie diesen Wert abrufen, müssen Sie wissen, zu welcher Zeitzone er gehört.

Wenn Sie also in Ihrem Fall einen Wert in einem DATETIME-Feld speichern, stellen Sie sicher, dass es Zeit für Tansania ist. Wenn Sie es dann herausbringen, ist es Zeit für Tansania. Yay!

Die haarige Frage lautet nun: Wenn ich ein INSERT / UPDATE mache, wie stelle ich sicher, dass der Wert Tansania-Zeit ist? Zwei Fälle:

  1. Das tust du INSERT INTO table (dateCreated) VALUES (CURRENT_TIMESTAMP or NOW()).

  2. Sie tun dies INSERT INTO table (dateCreated) VALUES (?)und geben die aktuelle Zeit in Ihrem Anwendungscode an.

FALL 1

MySQL wird die aktuelle Zeit in Anspruch nehmen. Nehmen wir an, es ist '2019-01-01 20:00:00' Zeit in Tansania. Dann konvertiert MySQL es in UTC , das auf '2019-01-01 17:00:00' erscheint, und speichert diesen Wert im Feld.

Wie können Sie also die Zeit in Tansania, die '20: 00: 00 'ist, auf dem Feld speichern? Es ist nicht möglich. Ihr Code muss beim Lesen aus diesem Feld mit der UTC-Zeit rechnen.

FALL 2

Dies hängt davon ab, als welche Art von Wert Sie übergeben ?. Wenn Sie die Zeichenfolge '2019-01-01 20:00:00' übergeben, ist das gut für Sie, genau das wird in der Datenbank gespeichert. Wenn Sie ein Date-Objekt übergeben, hängt dies davon ab, wie der Datenbank-Treiber dieses Date-Objekt interpretiert und welche Zeichenfolge 'YYYY-MM-DD HH: mm: ss' MySQL zur Speicherung zur Verfügung stellt. Die Dokumentation des DB-Treibers sollte es Ihnen sagen.

Sarsaparille
quelle
6

Alle von Ihnen beschriebenen Symptome deuten darauf hin, dass Sie MySQL niemals mitteilen, welche Zeitzone verwendet werden soll, sodass standardmäßig die Systemzone verwendet wird. Denken Sie darüber nach: Wenn alles, was es hat '2011-03-13 02:49:10', wie kann es erraten, dass es ein lokales tansanisches Datum ist?

Soweit ich weiß, bietet MySQL keine Syntax zur Angabe von Zeitzoneninformationen in Datumsangaben. Sie müssen es pro Verbindung ändern . etwas wie:

SET time_zone = 'EAT';

Wenn dies nicht funktioniert (um benannte Zonen zu verwenden, muss der Server dafür konfiguriert sein und dies ist häufig nicht der Fall), können Sie UTC-Offsets verwenden, da Tansania zum Zeitpunkt des Schreibens keine Sommerzeit einhält , aber natürlich es ist nicht die beste Option:

SET time_zone = '+03:00';
Álvaro González
quelle
15
Sie können jedoch nicht einfach einen UTC-Offset verwenden, da die Sommerzeit ignoriert wird. Ihre Zeit könnte leicht eine Stunde frei werden, mit wenig Hoffnung, sie jemals zu korrigieren! Ich kenne Funktionen in SQL Server, die UTC in Ihre Zeitzone konvertieren, aber keine willkürliche Verschiebung der Sommerzeit berücksichtigen. In UTC Nullpunktverschiebung speichern und in der Anwendung konvertieren - dies löst viele Probleme sehr sauber, aber Sie werden verdammt viel Zeit haben, Daten in Ad-hoc-Abfragen richtig zu konvertieren.
Sean Anderson
Jetzt ist es möglich, mindestens den Zeitzonenversatz direkt im Datum anzugeben, z. B.: '2020-01-01 10: 10: 10 + 05: 30' (von dev.mysql.com/doc/refman/8.0/ de / datetime.html ). Da der Zeitzonenversatz für ein bestimmtes Datum und eine bestimmte Uhrzeit gilt, spielt es keine Rolle, dass keine bestimmte Zeitzone (und damit deren Sommerzeitregeln) angegeben wird. Denn auf diese Weise legen Sie genau fest, wie UTC ab diesem Datum berechnet werden kann, und MySQL speichert es als UTC in seiner Datenbank. Eine Zeitzone wie "EAT" ist nur erforderlich, wenn Sie UTC für mehrere Daten (DST und nicht DST) berechnen möchten.
Dominique
0

Ich war auch einmal mit einem solchen Problem konfrontiert, bei dem ich Daten speichern musste, die von verschiedenen Mitarbeitern verwendet wurden, und am Ende die Zeit in Unix-Zeitstempelform gespeichert habe, die die Anzahl der Sekunden seit Januar 1970 darstellt, was ein ganzzahliges Format ist. Beispiel heutiges Datum und Uhrzeit in Tansania ist das, Friday, September 13, 2019 9:44:01 PMwas beim Speichern unter Unix-Zeitstempel wäre1568400241

Verwenden Sie jetzt beim Lesen der Daten einfach PHP oder eine andere Sprache und extrahieren Sie das Datum aus dem Unix-Zeitstempel. Ein Beispiel mit PHP wird sein

echo date('m/d/Y', 1568400241);

Dies macht es sogar einfacher, Daten mit anderen Mitarbeitern an verschiedenen Standorten zu speichern. Sie können das Datum einfach in einen Unix-Zeitstempel mit ihrem eigenen gmt-Offset konvertieren und in einem ganzzahligen Format speichern. Bei der Ausgabe wird dies einfach mit einem konvertiert

Geoff
quelle
Technisch gesehen speichern Sie keine Daten mit Zeitzoneninformationen - Sie verwerfen die Zeitzoneninformationen. Das ist im Grunde, was native Datumstypen bereits in MySQL tun ;-)
Álvaro González