Timezone

From Cloudrexx Development Wiki
Jump to: navigation, search

Notwendige Vorkenntnisse

http://de.wikipedia.org/wiki/UTC

Problem

Alle Zeitfunktionen im PHP und MySQL verwenden eine Zeitzone um ein Datum oder eine Uhrzeit generieren zu können. Jeder Server hat eine Standard-Zeitzone definiert, welche von den entsprechenden PHP- oder MySQL-Funktionen verwendet wird. Diese Standard-Zeitzone entspricht normalerweise dem Standort des Servers und kann bei bedarf selbst festgelegt werden. Wenn dort eine Zeitzone verwendet wird, die Sommer- und Winterzeit (DST - Daylight Savings Time) unterscheidet, so werden Daten im Sommer und im Winter u.U. nicht gleich berechnet.

Zudem sollte der Benutzer sich Daten in seiner Zeitzone anzeigen lassen können. Dazu muss das Datum für den jeweiligen Benutzer umgerechnet werden.

Lösung

Die Unterscheidung zwischen folgenden Zeitzonen führt zur Lösung:

  • user: Zeitzone des Benutzers (/zur Ausgabe)
  • intern: Zeitzone für Berechnungen
  • db: Zeitzone für Datenablage

Die Zeitzonen können theoretisch auch alle gleich sein. Wichtig ist, dass die Zeitzone zur Datenablage (db) keine DST beachtet (z.B. UTC).

Seit Contrexx 3.0.0 wird während der Installation eine Zeitzone ausgewählt. Da Contrexx/Cloudrexx (noch) nicht unterscheidet zwischen der Zeitzone des Benutzers und für die Berechnungen, sind diese identisch gesetzt. Lediglich beim Auslesen aus der Datenablage findet eine Zeitzonenumrechnung statt.

Best Practice

  • Die Zeitzone der Datenbankablage selbst festlegen (nicht die Zeitzone des Datenbankservers verwenden!)
  • Als Zeitzone für die Datenbankablage eine Zeitzone ohne DST verwenden (WICHTIG!)
  • Nur PHP-Zeitfunktionen und keine MySQL-Zeitfunktionen verwenden: Somit können Zeit-Unstimmigkeiten zwischen PHP- und MySQL-Server vermieden werden.
  • Die MySQL-Datentypen DATETIME/DATE/TIME verwenden, TIMESTAMP vermeiden. Der Datentyp TIMESTAMP unterliegt dem y2038-Problem[1][2].
    • DATE und TIME (allenfalls in Kombination): Bei Referenzen auf einen ortsunabhängigen Zeitpunkt. Beispiele:
      • weltweit gleiche (/von der Zeitzone unabhängige) Öffnungszeiten von 9-17 Uhr
      • Silvester am 31.12. um 24:00
    • DATETIME: Bei ortsabhängigen Zeipunkten. Beispiel:
      • Silvesterparty in Zürich um 23 Uhr
  • Beim Exportieren der Datenbank mittels mysqldump ist standardmässig die Option --tz-utc gesetzt, welche bewirkt, dass Daten von Feldern mit dem Datentyp TIMESTAMP in UTC exportiert werden. Damit nach dem Importieren des Dumps die Daten wieder in der richtigen Zeitzone vorliegen, muss überprüft werden, ob im Dump folgendes enthalten ist: SET TIME_ZONE='+00:00';.

Cloudrexx

Für Models sollten nur die MySQL-Datentypen DATETIME, DATE und TIME verwendet werden (siehe Begründung dazu oben).

Damit die Konvertierung (zwischen den Zeitzonen der verschiedenen Ebenen Benutzer (user), Berechnung (intern) und Datenablage (db)) immer korrekt erfolgt, bietet der DateTime Component folgende Helper an:

Initialisierung

Aus Benutzereingabe (GUI)

Erstellt eine neue DateTime Instanz aus einer Benutzereingabe (in der Zeitzone der Benutzerebene user) und konvertiert diese zur Weiterverarbeitung in die Zeitzone der Berechnungsebene (intern):

$datetime = $this->cx->getComponent('DateTime')->createDateTimeForUser($_GET['time']);
$datetime->user2intern();

Für Benutzerausgabe

Vor der Ausgabe an den Benutzer muss eine DateTime Instanz in die Zeitzone der Benutzerebene (user) konvertiert werden:

// neue Instanz anlegen in der Zeitzone der Berechnungsebene (intern)
$datetime = new \DateTime($date);

// Ausführung div. Operationen auf $datetime
...

// Konvertierung in Zeitzone der Benutzerebene
$this->cx->getComponent('DateTime')->intern2user($datetime);

// $datetime ist nun bereit für die Benutzerausgabe
Tipp: Um die aktuelle Zeit (inkl. Datum) direkt für die Benutzerausgabe aufzubereiten kann createDateTimeForUser() ohne Argument aufgerufen werden:
$now = $this->cx->getComponent('DateTime')->createDateTimeForUser();

Aus der Datenbank

Erstellt eine neue DateTime Instanz aus einem Wert aus der Datenbank (in der Zeitzone der Datenbankebene db) und konvertiert diese zur Weiterverarbeitung in die Zeitzone der Berechnungsebene (intern):

$datetime = $this->cx->getComponent('DateTime')->createDateTimeForDb($dbValue);
$this->cx->getComponent('DateTime')->db2intern($datetime);

In die Datenbank

Vor der Speicherung einer DateTime Instanz muss diese in die Zeitzone der Datenbankebene (db) konvertiert werden:

// neue Instanz anlegen in der Zeitzone der Berechnungsebene (intern)
$datetime = new \DateTime($date);

// Ausführung div. Operationen auf $datetime
...

// Konvertierung in Zeitzone der Datenbankebene
$this->cx->getComponent('DateTime')->intern2db($datetime);

// $datetime ist nun bereit für die Speicherung in der Datenbank
Tipp: Um die aktuelle Zeit (inkl. Datum) direkt für die Ablage in die Datenbank aufzubereiten kann createDateTimeForDb() ohne Argument aufgerufen werden:
$now = $this->cx->getComponent('DateTime')->createDateTimeForDb();

Konvertierung

Zur Konvertierung einer DateTime Instanz zwischen den Zeitzonen der verschiedenen Ebenen stehen folgende Methoden bereit:

Ebene Benutzer (user) Berechnung (intern) Datenbank (db)
Benutzer (user) -
$this->cx->getComponent('DateTime')->user2intern($datetime)
$this->cx->getComponent('DateTime')->user2db($datetime)
Berechnung (intern)
$this->cx->getComponent('DateTime')->intern2user($datetime)
-
$this->cx->getComponent('DateTime')->intern2db($datetime)
Datenbank (db)
$this->cx->getComponent('DateTime')->db2user($datetime)
$this->cx->getComponent('DateTime')->db2intern($datetime)
-

Zeitzonen-Datenbank

MySQL

Wenn MySQL auf einem UNIX-System installiert ist, so wird auf dessen Zeitzonen-Datenbank zugegriffen, welche sich in den meisten Fällen unter "/usr/share/zoneinfo" befindet. Ist MySQL aber auf einem Windows-System installiert, steht diese Zeitzonen-Datenbank nicht zur Verfügung. In diesem Fall gibt es zwei Möglichkeiten:
a) Die Zeitzonen-Datenbank wird manuell importiert (weitere Informationen unter http://dev.mysql.com/downloads/timezones.html und http://dev.mysql.com/doc/refman/5.1/en/mysql-tzinfo-to-sql.html)
b) Anstelle des Namens (z. B. "Europe/Zurich") wird der Offset (z. B. "+02:00") der Zeitzone gesetzt.

Cloudrexx verwendet Möglichkeit b).

PHP

PHP hat eine Zeitzonen-Datenbank integriert. Demzufolge spielt es keine Rolle, ob PHP auf einem UNIX- oder Windows-System installiert ist.

Olsen-Datenbank

PHP als auch UNIX verwenden die Zeitzonen-Datenbank "Olsen" (weitere Informationen unter http://en.wikipedia.org/wiki/Tz_database). Demzufolge können Zeitzonen-Unstimmigkeiten zwischen PHP und MySQL ausgeschlossen werden.

Hinweise

  • https://bugs.mysql.com/bug.php?id=12654
  • https://mariadb.com/kb/en/library/unix_timestamp/
  • Retrieved from "https://wiki.cloudrexx.com/index.php?title=Timezone&oldid=27639"