# HowTo: Der FHIR `date`-Suchparameter – Präfixe, Intervalle und Kombinationen richtig verstehen Wer FHIR-Server baut oder abfragt, stolpert früher oder später über den `date`-Suchparameter. Auf den ersten Blick sieht er simpel aus – `?date=2023-06-15` – aber sobald Präfixe wie `ge`, `sa` oder `ap` ins Spiel kommen und mehrere `date`-Parameter kombiniert werden, wird es schnell unübersichtlich. Dieser Artikel bringt Ordnung rein: Schritt für Schritt, mit Zeitleisten-Grafiken zu jedem Fall — und als Kurzreferenz zum schnellen Nachschlagen. ## Inhaltsverzeichnis - [Kurzreferenz: Alle Präfixe auf einen Blick](#kurzreferenz-alle-präfixe-auf-einen-blick) - [1. Das Grundprinzip: FHIR vergleicht Intervalle, keine Zeitpunkte](#1-das-grundprinzip-fhir-vergleicht-intervalle-keine-zeitpunkte) - [2. Die neun Präfixe im Überblick](#2-die-neun-präfixe-im-überblick) - [3. Matching gegen `Period` und `Range`](#3-matching-gegen-period-und-range) - [4. Offene (unvollständige) Periods](#4-offene-unvollständige-periods) - [5. Mehrere `date`-Parameter kombinieren](#5-mehrere-date-parameter-kombinieren) - [6. Die vollständige 3×3-Matrix aller Bereichs-Kombinationen](#6-die-vollständige-3×3-matrix-aller-bereichs-kombinationen) - [7. Sonderfälle: redundant oder widersprüchlich](#7-sonderfälle-wenn-kombinationen-redundant-oder-widersprüchlich-werden) - [8. Praxis-Empfehlungen zum Schluss](#8-praxis-empfehlungen-zum-schluss) --- ## Kurzreferenz: Alle Präfixe auf einen Blick Für den schnellen Blick zwischendurch — Details und Grafiken zu jeder Zeile folgen in den Abschnitten unten. | Präfix | Name | Kurzregel | Beispiel | |---|---|---|---| | *(keins)* | implizit `eq` | Precision des Werts bestimmt das Intervall | `date=2023-06-15` | | `eq` | gleich | Resource-Intervall vollständig im Such-Intervall | `date=eq2023-06-15` | | `ne` | ungleich | Komplement von `eq` | `date=ne2023-06-15` | | `gt` | größer als | Ragt über das Ende hinaus (Overlap genügt) | `date=gt2023-06-15` | | `lt` | kleiner als | Beginnt vor dem Anfang (Overlap genügt) | `date=lt2023-06-15` | | `ge` | größer/gleich | `gt` oder vollständig enthalten (`eq`) | `date=ge2023-06-15` | | `le` | kleiner/gleich | `lt` oder vollständig enthalten (`eq`) | `date=le2023-06-15` | | `sa` | starts after | Vollständig danach, **keine** Überlappung | `date=sa2023-06-15` | | `eb` | ends before | Vollständig davor, **keine** Überlappung | `date=eb2023-06-15` | | `ap` | approximately | Überlappung + ähnliche Größenordnung | `date=ap2023-06-15` | **Fehlender `start`/`end` bei `Period`:** fehlender `start` = `-∞`, fehlendes `end` = `+∞` → `sa` kann bei fehlendem `start` nie matchen, `eb` kann bei fehlendem `end` nie matchen (Details in [Abschnitt 4](#4-offene-unvollständige-periods)). --- ## 1. Das Grundprinzip: FHIR vergleicht Intervalle, keine Zeitpunkte Der wichtigste gedankliche Reset zuerst: FHIR behandelt Datumswerte nie als exakten Zeitpunkt, sondern immer als **Intervall**. Wie breit dieses Intervall ist, hängt von der Precision des angegebenen Werts ab: - `2023` → das ganze Jahr 2023 - `2023-06` → der ganze Juni 2023 - `2023-06-15` → der ganze 15. Juni 2023 (00:00–24:00 Uhr) - `2023-06-15T10:00:00` → (fast) ein exakter Zeitpunkt Jeder Vergleichsoperator vergleicht anschließend **zwei Intervalle** miteinander: das Such-Intervall aus deinem Query-Parameter und das Resource-Intervall aus dem Feld der Ressource (z. B. `Patient.birthDate` oder `Encounter.period`). ### Ohne Präfix = `eq`, aber die Precision entscheidet mit Gibst du kein Präfix an, nimmt der Server `eq` an. Das Resource-Datum muss dann **vollständig** innerhalb deines Such-Intervalls liegen. Je gröber die Precision, desto breiter das Intervall – und desto mehr Resource-Werte passen hinein: ![Datum ohne Präfix](fhir-date-ohne-praefix.svg) `date=2023-06-15` matched nur diesen einen Tag. `date=2023-06` matched dagegen jeden Tag im Juni – auch den 15. Das ist der häufigste Stolperstein für Einsteiger: eine „ungenaue" Suchanfrage ist nicht strenger, sondern lockerer. --- ## 2. Die neun Präfixe im Überblick FHIR definiert insgesamt neun Präfixe für `date`. Jeder davon beschreibt eine andere Beziehung zwischen Such-Intervall (rot gestrichelt in der Grafik) und Treffer-Bereich (grün/farbig schattiert): ![Alle Präfixe](fhir-date-alle-praefixe.svg) | Präfix | Name | Bedeutung | |---|---|---| | `eq` | gleich | Resource-Intervall liegt vollständig im Such-Intervall | | `ne` | ungleich | Komplement von `eq` | | `gt` | größer als | Resource-Intervall **ragt über** das Ende des Such-Intervalls hinaus | | `lt` | kleiner als | Resource-Intervall **beginnt vor** dem Anfang des Such-Intervalls | | `ge` | größer/gleich | `gt` **oder** vollständig im Such-Intervall enthalten (`eq`) | | `le` | kleiner/gleich | `lt` **oder** vollständig im Such-Intervall enthalten (`eq`) | | `sa` | starts after | Beginnt vollständig **nach** dem Ende des Such-Intervalls, keine Überlappung | | `eb` | ends before | Endet vollständig **vor** dem Beginn des Such-Intervalls, keine Überlappung | | `ap` | approximately | Überlappung, plus ähnliche Größenordnung der Intervalle | **Wichtig — Containment vs. Overlap:** Nur `eq` (und implizit `sa`/`eb`) verlangen, dass das Resource-Intervall **vollständig** innerhalb bzw. **vollständig getrennt** vom Such-Intervall liegt. Bei `gt`, `lt`, `ge` und `le` reicht dagegen eine **Überlappung** — das Resource-Intervall muss nicht komplett außerhalb des Suchbereichs liegen, es genügt, dass es über die jeweilige Grenze hinausragt. Details dazu im nächsten Abschnitt. --- ## 3. Matching gegen `Period` und `Range` Die Grafiken oben zeigen das Resource-Datum als schmalen roten Marker – das passt für einfache `date`/`dateTime`-Felder wie `Patient.birthDate`. Sobald das Zielfeld selbst ein **Intervall** ist (`Period`, z. B. `Encounter.period`, `Condition.onsetPeriod`, oder `Range`), wird die Unterscheidung zwischen **Containment** (vollständiges Enthaltensein) und **Overlap** (bloße Überlappung) entscheidend: - **`eq`** verlangt Containment: das gesamte Resource-Intervall muss im Such-Intervall liegen. - **`gt` / `lt` / `ge` / `le`** verlangen nur Overlap: es reicht, wenn das Resource-Intervall über die jeweilige Grenze hinausragt – auch wenn ein Teil davon auf der "falschen" Seite liegt. - **`sa` / `eb`** verlangen das Gegenteil von Overlap: das Resource-Intervall muss vollständig und ohne jede Überlappung auf der jeweiligen Seite liegen. Bei einem einfachen Zeitpunkt-Feld (kein `Period`) fällt dieser Unterschied praktisch nicht auf, da Resource-Start und -Ende (fast) identisch sind — dort verhalten sich `gt` und `sa` de facto gleich. Der Unterschied wird erst bei echten Zeitspannen relevant. Das ist auch der Grund, warum `sa`/`eb` laut Spezifikation in erster Linie für `Period`/`Range`-Werte gedacht sind. **Beispiel:** Ein Encounter läuft vom 10. bis 20. Juni. Die Suche `date=gt2023-06-15` soll ihn finden, obwohl der Encounter *vor* dem 15. begonnen hat — entscheidend ist nur, dass er über den 15. hinausreicht (Overlap mit dem Bereich "nach dem 15."). Bei `date=sa2023-06-15` würde derselbe Encounter dagegen **nicht** matchen, weil er nicht vollständig nach dem 15. liegt — genau das ist der praktische Unterschied zwischen `gt` und `sa`. Die folgende Grafik macht das für alle vier "einseitigen" Präfixe (`gt`, `sa`, `lt`, `eb`) an denselben drei Beispiel-Periods sichtbar — einer Period komplett davor, einer, die die Suchgrenze überlappt, und einer komplett danach: ![Period/Range Matching](fhir-date-period-range-matching.svg) Man sieht deutlich: `gt` und `lt` (blau schattierter Bereich, durchgezogene Grenzlinie) lassen die überlappende Period B als Treffer durch, während `sa` und `eb` (gestrichelte Grenzlinie) sie ablehnen — nur eine Period, die komplett auf der richtigen Seite liegt, zählt dort. | Encounter-Zeitraum | `date=gt2023-06-15` | `date=sa2023-06-15` | |---|---|---| | 10.–20. Juni (überlappt die Grenze) | ✅ Treffer (Overlap) | ❌ kein Treffer (nicht vollständig danach) | | 16.–20. Juni (komplett danach) | ✅ Treffer | ✅ Treffer | | 01.–10. Juni (komplett davor) | ❌ kein Treffer | ❌ kein Treffer | --- ## 4. Offene (unvollständige) Periods In der Praxis sind Periods oft **nicht abgeschlossen** — z. B. ein laufender Encounter ohne `end`, oder ein importierter Datensatz mit unbekanntem `start`. Der FHIR-Standard regelt das eindeutig: > Ein fehlender unterer Rand (`start`) gilt implizit als **kleiner als jedes > reale Datum** (also `-∞`). Ein fehlender oberer Rand (`end`) gilt implizit > als **größer als jedes reale Datum** (also `+∞`). Das hat spürbare Konsequenzen, gerade für `sa` und `eb`: - Ein laufender Encounter (`start` gesetzt, `end` fehlt → `end = +∞`) kann **niemals** `eb` (ends before) matchen — sein Ende liegt ja nie vor irgendeinem endlichen Datum. Er matcht aber sehr wohl `gt`, weil sein (unendliches) Ende immer über jede Suchgrenze hinausragt. - Ein Encounter mit unbekanntem Start (`start` fehlt → `start = -∞`) kann **niemals** `sa` (starts after) matchen — sein Beginn liegt ja nie nach irgendeinem endlichen Datum. Er matcht aber `lt`, weil sein (unendlicher) Anfang immer vor jede Suchgrenze hineinreicht. - Eine Period ganz ohne `start` und `end` überlappt praktisch **jeden** overlap-basierten Filter (`gt`, `lt`, `ge`, `le`) — sie „reicht" ja per Definition über jede Grenze hinaus. Dieselben vier Präfixe wie in [Abschnitt 3](#3-matching-gegen-period-und-range), jetzt mit offenen statt geschlossenen Periods: ![Offene Periods Matching](fhir-date-offene-periods.svg) **Praktische Konsequenz:** Wenn du „laufende" Ressourcen (z. B. aktive Encounter, offene Verordnungen) gezielt aus einer Zeitraumsuche ausschließen willst, reicht `eb`/`sa` allein nicht — du musst zusätzlich nach dem Status oder explizit nach `end:missing=true`/`start:missing=true` filtern, je nachdem, was dein Server unterstützt. --- ## 5. Mehrere `date`-Parameter kombinieren In der Praxis reicht ein einzelner Präfix selten – meist willst du einen **Bereich** definieren. FHIR-Server verknüpfen mehrere `date`-Parameter in derselben Query immer per **UND**. Vier typische Muster: ![Kombinationen](fhir-date-kombinationen.svg) | Kombination | Beispiel | Charakter | |---|---|---| | `ge` + `le` | `ge2023-01-01&le2023-12-31` | Geschlossenes Intervall (beide Grenzen inklusive) | | `gt` + `lt` | `gt2023-01-01<2023-12-31` | Offenes Intervall (beide Grenzen exklusive) | | `sa` + `eb` | `sa2023-01-01&eb2023-12-31` | Strikt getrennter Bereich, primär für Period-Werte | | `ge` + `lt` | `ge2023-06-01<2023-07-01` | Halboffenes Intervall `[start, ende)` – Standardmuster für „genau ein Monat" | Wenn du in Timestamp-lastigen ETL-Pipelines mit `[start, ende)` arbeitest, kommt dir `ge` + `lt` bekannt vor – es ist exakt dasselbe Muster wie bei klassischen Zeitfenster-Queries in SQL. --- ## 6. Die vollständige 3×3-Matrix aller Bereichs-Kombinationen Die vier Muster oben sind nur die gebräuchlichsten. Tatsächlich lässt sich **jede** Untergrenze mit **jeder** Obergrenze kombinieren: - Untergrenzen: `ge`, `gt`, `sa` - Obergrenzen: `le`, `lt`, `eb` Das ergibt 3 × 3 = **9 mögliche Kombinationen**, alle spec-konform: ![3x3 Matrix](fhir-date-matrix-3x3.svg) Sie unterscheiden sich nur darin, ob die jeweilige Grenze inklusiv (durchgezogene Linie) oder exklusiv (gestrichelte Linie) ist, und ob es sich um einen einfachen Werte-Vergleich (`ge`/`gt`/`le`/`lt`) oder einen strengen Period-Vergleich (`sa`/`eb`) handelt. Es gibt **keine explizite Ausschluss-Regel** im FHIR-Standard – jede Zelle dieser Matrix ist eine gültige, funktionierende Query. --- ## 7. Sonderfälle: Wenn Kombinationen redundant oder widersprüchlich werden Nicht jede syntaktisch gültige Kombination ist auch praktisch sinnvoll. Drei Kategorien solltest du kennen, bevor du sie versehentlich in einer generierten Query landen lässt: ![Sonderfälle](fhir-date-sonderfaelle.svg) ### a) Redundante Kombinationen Ändern nichts am Ergebnis, sind aber technisch kein Fehler: - `eq` + eine Grenze, die vom `eq`-Intervall ohnehin erfüllt wird (`eq2023-06-15&ge2020-01-01`) – die Zusatzbedingung ist überflüssig. - Zwei Bedingungen desselben Richtungstyps (`ge2020-01-01&ge2023-01-01`) – nur die strengere (spätere) Untergrenze wirkt effektiv. ### b) Kombinationen mit garantiert leerem Ergebnis Strukturell unmöglich zu erfüllen: - `eq` + `ne` auf denselben Wert (`eq2023-06-15&ne2023-06-15`) – sich gegenseitig ausschließende Bedingungen. - Untergrenze liegt zeitlich nach der Obergrenze (`ge2023-06-01&le2023-01-01`) – kein Datum erfüllt beides gleichzeitig. Der Server wird solche Queries **syntaktisch akzeptieren** und einfach eine leere Ergebnismenge zurückgeben – kein Fehler, aber auch keine hilfreiche Rückmeldung. Es lohnt sich, so etwas clientseitig vorab zu validieren. ### c) Ungewöhnliche, aber gültige Sonderfälle - **Drei oder mehr `date`-Parameter**, z. B. ein Jahresbereich mit ausgeschlossenem Einzeltag: `ge2023-01-01&le2023-12-31&ne2023-06-15`. - **`ap` kombiniert mit einer Grenze**, z. B. `ap2023-06-15&ge2023-01-01` – selten genutzt, da `ap` selbst schon unscharf ist und die Toleranzberechnung serverabhängig bleibt. --- ## 8. Praxis-Empfehlungen zum Schluss - Für einfache Bereichsfilter ist **`ge` + `lt`** (halboffenes Intervall) meist das robusteste Muster – es vermeidet Rundungsprobleme an der Precision-Grenze (kein Off-by-one bei „bis einschließlich 31.12."). - **`sa`/`eb`** bewusst einsetzen, wenn du bei `Period`/`Range`-Feldern eine *vollständige* Trennung brauchst (siehe [Abschnitt 3](#3-matching-gegen-period-und-range)) – z. B. „Encounter vollständig nach Entlassung X". Für einen simplen „ab Datum X"-Filter ist meist `ge`/`gt` gemeint, nicht `sa`. - Bei `Period`-Feldern mit möglichen offenen Enden (siehe [Abschnitt 4](#4-offene-unvollständige-periods)) nicht vergessen: `sa`/`eb` greifen dort strukturell nie — ggf. zusätzlich `:missing` oder Status filtern. - Vor dem Absetzen einer Multi-Parameter-Query lohnt sich ein kurzer Check auf logische Konsistenz zwischen Unter- und Obergrenze, um leere Ergebnisse durch Konfigurationsfehler in der eigenen Pipeline zu vermeiden. Mit der Kurzreferenz oben und den sechs Grafiken im Hinterkopf – ohne Präfix, alle neun Präfixe einzeln, Containment vs. Overlap bei Period/Range (offen wie geschlossen), typische Kombinationen, die volle 3×3-Matrix und die Sonderfälle – sollte die nächste `date`-Query im FHIR-Suchparameter kein Rätsel mehr sein.