Mysql
 sql >> Datenbank >  >> RDS >> Mysql

PHP, MySQL und Zeitzonen

Diese Antwort wurde aktualisiert, um dem Kopfgeld Rechnung zu tragen. Die ursprüngliche, unbearbeitete Antwort befindet sich unter der Zeile.

Fast alle vom Kopfgeldbesitzer hinzugefügten Fragepunkte beziehen sich darauf, wie MySQL- und PHP-Datetimes im Kontext von Zeitzonen interagieren sollten.

MySQL ist immer noch erbärmlich Zeitzonenunterstützung , was bedeutet, dass die Intelligenz PHP-seitig sein muss.

  • Stellen Sie Ihre MySQL-Verbindungszeitzone ein zu UTC, wie im obigen Link dokumentiert. Dadurch werden alle von MySQL verarbeiteten Datetimes verursacht, einschließlich NOW() , vernünftig gehandhabt werden.
  • Verwenden Sie immer DATETIME , verwenden Sie niemals TIMESTAMP es sei denn, Sie fordern ausdrücklich das spezielle Verhalten in einem TIMESTAMP . Dies ist weniger schmerzhaft als früher.
    • Ist in Ordnung um die Unix-Epochenzeit als Ganzzahl zu speichern, wenn Sie haben zu, wie zum Beispiel für Legacy-Zwecke. Die Epoche ist UTC.
    • Das bevorzugte Datetime-Format von MySQL wird mithilfe der PHP-Datumsformatzeichenfolge Y-m-d H:i:s erstellt
  • Konvertiere alle PHP setzt Datetimes auf UTC, wenn sie in MySQL gespeichert werden, was eine triviale Sache ist, wie unten beschrieben
  • Datumswerte, die von MySQL zurückgegeben werden, können sicher an den PHP-DateTime-Konstruktor übergeben werden. Achten Sie darauf, auch eine UTC-Zeitzone anzugeben!
  • Konvertieren Sie die PHP DateTime bei Echo in die lokale Zeitzone des Benutzers , nicht früher. Zum Glück berücksichtigen der DateTime-Vergleich und die Mathematik mit anderen DateTimes die Zeitzone, in der sich jeder befindet.
  • Sie sind immer noch den Launen der mit PHP bereitgestellten DST-Datenbank gewachsen. Halten Sie Ihre PHP- und OS-Patches auf dem neuesten Stand! Halten Sie MySQL im glückseligen UTC-Zustand, um eine potenzielle DST-Ärgerlichkeit zu beseitigen.

Das spricht die meisten an der Punkte.

Das Letzte ist ein Trottel:

  • Was sollte jemand tun, der zuvor Daten eingefügt hat (z. B. mit NOW() ), ohne sich Gedanken über die Zeitzone machen zu müssen, um sicherzustellen, dass alles konsistent bleibt?

Das ist ein echtes Ärgernis. Eine der anderen Antworten wies auf MySQLs CONVERT_TZ , obwohl ich es persönlich getan hätte, indem ich während Auswahlen und Aktualisierungen zwischen server-nativen und UTC-Zeitzonen hin- und hergesprungen wäre, weil ich so Hardcore bin.

Die App sollte auch in der Lage sein, die Sommerzeit für jeden Benutzer selbst entsprechend einzustellen/auszuwählen.

Sie müssen und sollten nicht Tun Sie dies in der Neuzeit.

Moderne Versionen von PHP haben die DateTimeZone Klasse, die die Möglichkeit beinhaltet, benannte Zeitzonen aufzulisten . Benannte Zeitzonen ermöglichen es dem Benutzer, seinen tatsächlichen Standort auszuwählen und das System automatisch zu aktivieren bestimmen ihre DST-Regeln basierend auf diesem Standort.

Sie können DateTimeZone mit DateTime kombinieren für einige einfache, aber leistungsstarke Funktionen. Sie können alle einfach speichern und verwenden Ihrer Zeitstempel in UTC standardmäßig , und konvertieren Sie sie in die angezeigte Zeitzone des Benutzers.

// UTC default
    date_default_timezone_set('UTC');
// Note the lack of time zone specified with this timestamp.
    $nowish = new DateTime('2011-04-23 21:44:00');
    echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 21:44:00
// Let's pretend we're on the US west coast.  
// This will be PDT right now, UTC-7
    $la = new DateTimeZone('America/Los_Angeles');
// Update the DateTime's timezone...
    $nowish->setTimeZone($la);
// and show the result
    echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 14:44:00

Durch die Verwendung dieser Technik wird das System automatisch Wählen Sie die richtigen DST-Einstellungen für den Benutzer aus, ohne den Benutzer zu fragen, ob er sich derzeit in DST befindet oder nicht.

Sie können eine ähnliche Methode verwenden, um das Auswahlmenü zu rendern. Sie können die Zeitzone für das einzelne DateTime-Objekt kontinuierlich neu zuweisen. Dieser Code listet beispielsweise die Zonen und ihre aktuellen Zeiten in diesem Moment auf:

$dt = new DateTime('now', new DateTimeZone('UTC')); 
foreach(DateTimeZone::listIdentifiers() as $tz) {
    $dt->setTimeZone(new DateTimeZone($tz));
    echo $tz, ': ', $dt->format('Y-m-d H:i:s'), "\n";
}

Sie können den Auswahlprozess erheblich vereinfachen, indem Sie etwas clientseitige Magie anwenden. Javascript hat einen fleckig, aber funktional Date-Klasse, mit einer Standardmethode, um den UTC-Offset in Minuten abzurufen . Sie können dies verwenden, um die Liste der wahrscheinlichen Zeitzonen einzugrenzen, unter der blinden Annahme, dass die Uhr des Benutzers richtig ist.

Vergleichen wir diese Methode damit, es selbst zu tun. Sie müssten eigentlich jedes Mal Datumsberechnungen durchführen Sie manipulieren eine Datumszeit und drängen dem Benutzer eine Auswahl auf, die ihn nicht wirklich interessiert. Das ist nicht nur suboptimal, es ist verrückt nach Fledermaus-Guano. Benutzer dazu zu zwingen anzugeben, wann sie Unterstützung für die Sommerzeit wünschen, führt zu Ärger und Verwirrung.

Wenn Sie außerdem das moderne PHP-Framework DateTime und DateTimeZone dafür verwenden möchten, müssen Sie veraltenes Etc/GMT... verwenden Zeitzonen-Strings anstelle von benannten Zeitzonen. Diese Zonennamen können aus zukünftigen PHP-Versionen entfernt werden, daher wäre es unklug, dies zu tun. Ich sage das alles aus Erfahrung.

tl;dr :Verwenden Sie das moderne Toolset, ersparen Sie sich die Schrecken der Datumsmathematik. Präsentieren Sie dem Benutzer eine Liste mit Namen Zeitzonen. Speichern Sie Ihre Daten in UTC , die in keiner Weise von der Sommerzeit beeinflusst werden. Konvertieren Sie Datums- und Uhrzeitangaben in die ausgewählte benannte Zeitzone des Benutzers auf dem Display , nicht früher.

Wie gewünscht, hier ist eine Schleife über die verfügbaren Zeitzonen, die ihren GMT-Offset in Minuten anzeigt. Ich habe hier Minuten ausgewählt, um eine unglückliche Tatsache zu demonstrieren:Nicht alle Offsets sind in ganzen Stunden! Einige schalten während der Sommerzeit tatsächlich eine halbe Stunde im Voraus statt eine ganze Stunde. Der resultierende Offset in Minuten sollte dem Date.getTimezoneOffset von Javascript entsprechen .

$utc = new DateTimeZone('UTC');
$dt = new DateTime('now', $utc); 
foreach(DateTimeZone::listIdentifiers() as $tz) {
    $local = new DateTimeZone($tz);
    $dt->setTimeZone($local);
    $offset = $local->getOffset($dt); // Yeah, really.
    echo $tz, ': ', 
         $dt->format('Y-m-d H:i:s'),
         ', offset = ',
         ($offset / 60),
         " minutes\n";
}