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.

DateFormat ist kein Ausweg. Der Parameter wirkt nur auf DATE-Felder, die als Text übertragen werden. DATETIME kommt unabhängig davon immer als Epoch-Zahl zurück, auch mit %H:%M:%S oder %z im Format.

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_timestamp des optimistischen Lockings, ein Round-Trip von OBJECT_MODIFYTIME

  • Workflow-Variablen des Typs DATETIME

  • JobParameterTypes.DATE_TIME auf 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_to_server_time(seconds)

Epoch-Wert → naive Wandzeit der Installation

server_time_to_epoch(value)

datetime → Epoch-Wert; Umkehrung der obigen Funktion, daher exakt im Round-Trip

to_server_time(value)

datetime → naive Wandzeit der Installation, für die Textform im DMS-XML