Zum Hauptinhalt springen

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).

Zwei getrennte Belange – halten Sie sie auseinander

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:

EingabeFestgelegt aufRolle
Referenzzeitzonedem Rollup-Archiv (ReferenceTimeZone) – siehe Stream Data ArchivesEine Eigenschaft des physischen Standorts der Serie. Macht die gespeicherten Kalender-Buckets (CalendarDay / Iso8601Week / CalendarMonth / CalendarYear) DST-korrekt. Wird mit dem Archiv provisioniert.
Abfrage-Zeitzoneder 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:

FeldWerteStandardBedeutung
timeZoneIANA-ID, z. B. Europe/Vienna(leer ⇒ UTC)Die Zone, an der Kalender-Rollups ausgerichtet und gegen die sie ausgewählt werden.
comparisonPolicyPerQuery / PerSeriesPerQueryWie 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 gespeicherte ReferenceTimeZone mit 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 der ReferenceTimeZone ihres Archivs auf („das lokale Gestern jedes Standorts"). Nutzt den bestehenden Pro-Quelle-Fan-out erneut.
Invariante der Grenzenkonsistenz

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 passende ReferenceTimeZone trä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:

TagLokaler Beginn → EndeUTC-FensterLänge
2026-03-29 (Umstellung vor)00:00 CET → 00:00 CEST2026-03-28T23:00Z → 2026-03-29T22:00Z23 h
2026-06-15 (kein Übergang)00:00 CEST → 00:00 CEST2026-06-14T22:00Z → 2026-06-15T22:00Z24 h
2026-10-25 (Umstellung zurück)00:00 CEST → 00:00 CET2026-10-24T22:00Z → 2026-10-25T23:00Z25 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 in TimeRangeUtils sind 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 resolveSeriesQuery gesendete timeZone. Siehe den MeshBoard-Zeitzonen-Leitfaden.
  • comparisonPolicy hat als Standard PerQuery; PerSeries ist ü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.