Zeitzone der enaio-Installation
enaio speichert jeden Zeitpunkt als Unix-Epoch-Zahl und berechnet diese Zahl in der Zeitzone des Servers. Das Protokoll überträgt diese Zeitzone nicht. Der Client kann sie deshalb nicht ableiten, er muss sie erfahren.
1. Das Problem
Ein DATETIME-Indexfeld wird als Wandzeit-Text geschrieben, ohne Zeitzone:
<Field internal_name="DateTimeField">15.06.2024 10:30:00</Field>
Gelesen wird es als Epoch-Zahl zurück, die der Server aus dieser Wandzeit in seiner eigenen Zeitzone berechnet hat:
<Field value="1718440200" internal_name="DateTimeField" datatype="DATETIME" dbname="zahl1" />
1718440200 ist 08:30 UTC, also 10:30 in Europe/Zurich. Wer diese Zahl in einer anderen
Zeitzone auflöst, erhält eine andere Uhrzeit als die, die im enaio-Client steht.
Historisch löste die Bibliothek das implizit: alle Umrechnungen liefen über
datetime.fromtimestamp() und datetime.timestamp(), also über die Zeitzone des
Client-Prozesses. Das funktioniert, solange dieser Prozess in der Zeitzone der Installation
läuft. Tut er das nicht, sind alle Zeitstempel um die Differenz verschoben, ohne dass irgendwo
ein Fehler auftritt.
|
|
2. Konfiguration
Einmal beim Start setzen, bevor der erste Job läuft:
from ecmind_blue_client import set_server_timezone
set_server_timezone("Europe/Zurich")
Oder per Umgebungsvariable, wenn der Anwendungscode nicht angefasst werden soll:
ECMIND_BLUE_SERVER_TIMEZONE=Europe/Zurich
Die Einstellung gilt prozessweit. Das ist beabsichtigt: die Zeitzone ist eine Eigenschaft der Installation, nicht einer Verbindung, und alle Pools eines Prozesses sprechen mit einer Installation.
Ein nicht auflösbarer Name wirft ValueError, statt stillschweigend auf die Host-Zeitzone
zurückzufallen. Ursache ist eine fehlende IANA-Zeitzonendatenbank. Unter Windows gibt es keine,
deshalb hängt die Bibliothek dort tzdata als Abhängigkeit an; in einem schlanken
Linux-Container muss das Paket nachinstalliert werden.
3. Wirkungsbereich
Betroffen sind alle Epoch-Umrechnungen:
-
DATETIME-Indexfelder, lesend und schreibend -
die Basisparameter
<Created>und<Modified>(obj.system.base_params) -
obj.system.deleted_at(OBJECT_DELETED) -
der
concurrency_timestampdes optimistischen Lockings, ein Round-Trip vonOBJECT_MODIFYTIME -
Workflow-Variablen des Typs
DATETIME -
JobParameterTypes.DATE_TIMEauf dem Draht, in beide Richtungen
Nicht betroffen sind Werte, die als Text übertragen werden (DATE-Indexfelder, Workflow-Datums-
und -Zeitvariablen) sowie Zeitstempel, die die Bibliothek schon immer als explizites UTC liest
(Administrationsinformationen, Prozessliste, last_modified von OS-Events). Diese bezeichnen
Zeitpunkte, keine Wandzeit der Installation.
4. Verhalten ohne Konfiguration
Unkonfiguriert bleibt es beim alten Verhalten: die Zeitzone des Host-Prozesses gilt. Für eine
Installation, die bereits mit passendem TZ betrieben wird, ändert sich also nichts. Neu ist
nur, dass man es explizit sagen kann, statt es dem Host aufzutragen.
5. Naive und zeitzonenbewusste Werte
Rückgabewerte sind naive datetime-Objekte in der Wandzeit der Installation. Naiv, damit
ein gelesener Wert unverändert zurückgeschrieben und mit anderen Feldwerten verglichen werden
kann, ohne naive und bewusste Werte zu mischen.
Beim Schreiben wird ein naiver Wert als Wandzeit der Installation gelesen. Ein zeitzonenbewusster Wert ist bereits eindeutig und wird exakt umgerechnet, sodass der ausgedrückte Zeitpunkt auch der gespeicherte ist.
from datetime import datetime, timezone
set_server_timezone("Europe/Zurich")
doc.DateTimeField = datetime(2024, 6, 15, 10, 30) # 10:30 im DMS
doc.DateTimeField = datetime(2024, 6, 15, 8, 30, tzinfo=timezone.utc) # ebenfalls 10:30
6. Konvertierungsfunktionen
Für eigene Auswertungen sind die Umrechnungen exportiert:
| Funktion | Bedeutung |
|---|---|
|
Epoch-Wert → naive Wandzeit der Installation |
|
|
|
|