Timezone-Aware Queries
Streamdaten werden immer in UTC gespeichert. Viele Fragen werden jedoch in ziviler (Wanduhr-)Zeit für einen bestimmten Ort gestellt: „Wie waren die Werte gestern? diese Woche? letzten Monat?" – gemeint sind die lokalen Tages-/Wochen-/Monatsgrenzen einer gewählten Zeitzone, nicht ein UTC-Tag, der gegen den lokalen verschoben ist.
Dies wird essenziell, sobald ein Diagramm Zählpunkte über Zonen hinweg vergleicht – ein Standort in Europe/Vienna neben einem in Europe/Lisbon. Eine einzelne UTC-Bucket-Grenze kann „gestern" nicht für beide repräsentieren. Timezone-Aware Queries lösen zivilzeitliche Bereiche auf und richten Rollup-Buckets an einer gewählten Zone aus – DST-korrekt –, während die Speicherung UTC bleibt (AB#4190).
Berechnung (Abfrage): welches UTC-Fenster „gestern in Europe/Vienna" ist und welches Rollup die zivilen Tage dieser Zone hält. Anzeige (UI): das Rendern der Diagrammachse in lokaler Zeit. Diese Seite befasst sich mit der Berechnung; die Anzeige ist ein Rendering-Belang, der unabhängig davon behandelt wird.
Woher die Zeitzone kommt
Zwei unabhängige Eingaben wirken zusammen:
| Eingabe | Festgelegt auf | Rolle |
|---|---|---|
| Referenzzeitzone | dem Rollup-Archiv (ReferenceTimeZone) – siehe Stream Data Archives | Eine Eigenschaft des physischen Standorts der Serie. Macht die gespeicherten Kalender-Buckets (CalendarDay / Iso8601Week / CalendarMonth / CalendarYear) DST-korrekt. Wird mit dem Archiv provisioniert. |
| Abfrage-Zeitzone | der Abfrage (timeZone) | Die Zone, in der die Abfrage beantwortet wird – wird verwendet, um relative Bereiche aufzulösen und das Kalender-Rollup auszuwählen, dessen zivile Tage übereinstimmen. |
Beide verwenden ausschließlich IANA-Namen (Europe/Vienna, Europe/Lisbon) – niemals feste Offsets, die zweimal im Jahr über DST hinweg falsch sind.
Eine Serienabfrage in einer Zone auflösen
Der auflösungsbewusste Serien-Resolver (streamData.resolveSeriesQuery sowie das MCP-Tool resolve_series_query) akzeptiert zwei optionale Eingaben:
| Feld | Werte | Standard | Bedeutung |
|---|---|---|---|
timeZone | IANA-ID, z. B. Europe/Vienna | (leer ⇒ UTC) | Die Zone, an der Kalender-Rollups ausgerichtet und gegen die sie ausgewählt werden. |
comparisonPolicy | PerQuery / PerSeries | PerQuery | Wie zivile Grenzen aufgelöst werden, wenn eine Abfrage Serien in verschiedenen Zonen umspannt. |
query {
streamData {
resolveSeriesQuery(input: {
baseArchiveRtId: "…"
from: "2026-01-01T00:00:00Z"
to: "2027-01-01T00:00:00Z"
targetPoints: 365
requiredAggregation: SUM
sourcePath: "Amount.Value"
timeZone: "Europe/Vienna"
comparisonPolicy: PerQuery
}) {
archiveRtId
effectiveBucketMs
points
signal
}
}
}
Der Resolver gibt das abzufragende Archiv und die effektive Bucket-Breite zurück; anschließend führen Sie die Downsampling-Abfrage gegen archiveRtId aus.
Vergleichsrichtlinie
PerQuery(Standard) – eine Abfragezone wird einheitlich angewendet. „Gestern" ist ein ziviler Tag in der Abfragezone. Ein Kalender-Rollup ist nur dann eine gültige Quelle, wenn seine gespeicherteReferenceTimeZonemit der Abfragezone übereinstimmt; ein Rollup, das die zivilen Tage einer anderen Zone hält, wird von der Auswahl ausgeschlossen (seine Buckets sind nicht die zivilen Tage dieser Abfrage).PerSeries– jede Serie löst ihren eigenen lokalen Tag in derReferenceTimeZoneihres Archivs auf („das lokale Gestern jedes Standorts"). Nutzt den bestehenden Pro-Quelle-Fan-out erneut.
Das Abfragefenster [from, to) und die Bucket-Grenzen des Rollups müssen gegen dieselbe Zone berechnet werden, sonst landen die Fensterkanten mitten in einem Bucket. Unter PerQuery verwenden beide die Abfragezone; unter PerSeries wird der Bereich pro Serie in der Zone dieser Serie aufgelöst. Dies verhindert, dass nebeneinanderliegende Diagramme „um Stunden verschoben" sind.
Kalender-Sprossen vs. Sub-Tages-Sprossen
- Ziviler Tag und gröber (
CalendarDay/Iso8601Week/CalendarMonth/CalendarYear): Ein Rollup, das eine passendeReferenceTimeZoneträgt, speichert bereits DST-korrekte lokale Buckets – ein gespeicherter Bucket ist eine zivile Einheit, direkt gelesen. - Sub-Tag, feste Größe (1 h und feiner,
FixedSize): zeitzonenunabhängig.DATE_BIN-Bins sind gleichmäßig von einer UTC-Epoche beabstandet; die Zone beeinflusst nur die Achsenbeschriftungen, nicht die Bins. Zivilgrenzen-Semantik ist ausschließlich eine Eigenschaft kalenderausgerichteter Rollups.
Findet eine Zivil-Tages-Abfrage kein Kalender-Rollup mit passender Zone, gibt der Resolver ein wahrheitsgetreues Signal (ResolutionLimited / NoSuitableRollup) zurück, statt stillschweigend falsch auszurichten – provisionieren Sie ein CalendarDay-Rollup in der erforderlichen Zone, um es zu ermöglichen.
DST-Behandlung
Ein lokaler Tag über einen DST-Übergang hinweg ist 23 h oder 25 h, niemals feste 24 h. Bucket-Grenzen werden an zivilen Grenzen berechnet, sodass ein DST-Tages-Bucket einfach breiter oder schmaler ist – niemals aufgeteilt, niemals dupliziert. Beispiel, Europe/Vienna:
| Tag | Lokaler Beginn → Ende | UTC-Fenster | Länge |
|---|---|---|---|
| 2026-03-29 (Umstellung vor) | 00:00 CET → 00:00 CEST | 2026-03-28T23:00Z → 2026-03-29T22:00Z | 23 h |
| 2026-06-15 (kein Übergang) | 00:00 CEST → 00:00 CEST | 2026-06-14T22:00Z → 2026-06-15T22:00Z | 24 h |
| 2026-10-25 (Umstellung zurück) | 00:00 CEST → 00:00 CET | 2026-10-24T22:00Z → 2026-10-25T23:00Z | 25 h |
Clientseitige relative Bereiche
Dashboards lösen relative Bereiche (dieses Jahr / dieser Monat / ein gewählter Tag) in ein absolutes [from, to)-Fenster auf, bevor sie sie an das Backend senden. Diese Auflösung muss in der gewählten IANA-Zone erfolgen, nicht in der eigenen Zone des Browsers – andernfalls ist „gestern in Europe/Lisbon" falsch, wenn es von einem Wiener Browser aus betrachtet wird.
TimeRangeUtils (Shared UI) löst Jahres-/Quartals-/Monats-/Tages-/benutzerdefinierte Grenzen für eine beliebige IANA-Zone auf – DST-korrekt und unabhängig vom Browser, unter Verwendung des nativen Intl – ohne zusätzliche Abhängigkeit. Die Übergabe von 'local' oder 'utc' behält das historische Verhalten bei.
// Same civil day, two zones → two different UTC windows (one hour apart):
TimeRangeUtils.getDayRange(2026, 5, 15, undefined, undefined, 'Europe/Vienna');
// from 2026-06-14T22:00:00Z (CEST, UTC+2)
TimeRangeUtils.getDayRange(2026, 5, 15, undefined, undefined, 'Europe/Lisbon');
// from 2026-06-14T23:00:00Z (WEST, UTC+1)
Die Speicherung bleibt unverändert
Zeilen bleiben in UTC; die Zeitzone ist ausschließlich ein Belang zur Abfragezeit. Nichts wird doppelt gebucketet, und es werden keine Zeitzonen-Metadaten auf historische Zeilen zurückgeschrieben.
Status & Einschränkungen
- Der Backend-Lesepfad (Resolver-
timeZone+comparisonPolicy, GraphQL + MCP) und die DST-korrekte Zivil-Tages-Auflösung inTimeRangeUtilssind implementiert. - MeshBoards unterstützen durchgängig eine explizite IANA-Zone pro Board: Der Time Filter-Tab bietet Local / UTC / Specific time zone (ein filterbares IANA-Dropdown), und die gewählte Zone treibt sowohl den Zivil-Tages-Bereich als auch – für auflösungsbewusste Streamdaten-Widgets – das an
resolveSeriesQuerygesendetetimeZone. Siehe den MeshBoard-Zeitzonen-Leitfaden. comparisonPolicyhat als StandardPerQuery;PerSeriesist über die GraphQL/MCP-API erreichbar, hat aber noch keinen eigenen Board-Umschalter.- Cascade-Kalender-Rollups (ein Rollup, der aus einem anderen Rollup gespeist wird, z. B. täglich→wöchentlich→monatlich) sind in der aktuellen Phase von der automatischen Auswahl ausgeschlossen, unabhängig von der Zeitzone.