Datum und Zeit
Pantarey speichert Daten als Text im ISO-Format und rechnet auf dem Server in UTC; der Browser zeigt sie in der Ortszeit der Person an. Wer diese zwei Regeln kennt, versteht fast alle Datumsfragen: warum ein Zeitpunkt in der Datenbank auf Z endet, warum sich ISO-Daten als Text vergleichen lassen und deutsche Daten nicht, und warum ein Timer-Start eine Zeitzone braucht. Diese Seite sammelt Speicherformate, Zeitzonen, alle Datumsfunktionen und das Zeitverhalten von Timern und Fristen an einem Ort.
Speicherformate
Im Daten Designer stehen in der Gruppe Datum & Zeit zwei Typen; einen eigenen Typ für eine Uhrzeit ohne Datum gibt es nicht.
| Typ | Beschreibung | Gespeichert | Beispiel | Im Formular |
|---|---|---|---|---|
| Datum | Tag, Monat, Jahr | JJJJ-MM-TT (Format date) |
2026-03-15 |
Kalenderfeld (dateInput); Vorbelegung today, today+7, today-1 |
| Zeitpunkt | Datum mit Uhrzeit | ISO-Zeitstempel in UTC mit Z (Format date-time) |
2026-03-15T09:30:00.000Z |
Datum und Uhrzeit in Ortszeit (dateTimeInput); Vorbelegung now |
Eine Dauer ist eine Zahl mit dem Zahlenformat Dauer, gespeichert in Millisekunden. Systemfelder wie Erstell- und Änderungszeitpunkt eines Datensatzes sind Zeitpunkte im selben ISO-Format.
Beim Zeitpunkt gibt die Person Ortszeit ein; gespeichert wird die Umrechnung nach UTC. 10:30 in Augsburg im März wird zu 09:30:00.000Z, im Juli zu 08:30:00.000Z. Das Datum ohne Uhrzeit bleibt, wie eingegeben.
Anzeige
Eingabefelder zeigen Datum und Uhrzeit so, wie es der Browser für seine Sprache vorsieht; Pantarey legt kein Anzeigeformat fest. Die Datensatz-Ansicht zeigt Zeitpunkte als TT.MM.JJJJ HH:mm in der Zeitzone des Browsers; der Process Explorer zeigt Zeitpunkte als TT.MM.JJJJ, HH:mm:ss.
Datumsfeld-Grenzen
In eigenen Formularen (Formly-JSON) begrenzen dateInput und dateTimeInput den erlaubten Bereich; im Daten Designer werden diese Eigenschaften nicht eingestellt.
Eigenschaft (props) |
Wirkung |
|---|---|
min, max |
feste Grenze: JJJJ-MM-TT, today, today+N, today-N; beim Zeitpunkt zusätzlich now oder eine Uhrzeit |
minKey, maxKey |
Schlüssel eines anderen Datumsfelds derselben Ebene (in einer Wiederholung: derselben Zeile); dessen Wert ist die Grenze. Gilt zusätzlich eine feste Grenze, zählt die strengere. |
followMin: true |
rückt das Feld aus minKey über den eigenen Wert hinaus, wird dieser übernommen; ein leeres Feld bleibt leer |
Der Kalender graut Tage außerhalb der Grenzen aus; ein getippter Wert außerhalb meldet „Darf nicht vor ‚Von‘ liegen.“, „Darf nicht nach ‚Bis‘ liegen.“, „Frühestens am 05.11.2026.“ oder „Spätestens am 30.11.2026.“ (beim Zeitpunkt „Frühestens 05.11.2026, 09:00.“). Der Server prüft die Grenzen beim Absenden erneut.
[
{ "key": "von", "type": "dateInput", "props": { "label": "Von", "required": true }, "defaultValue": "today" },
{ "key": "bis", "type": "dateInput", "props": { "label": "Bis", "required": true, "minKey": "von", "followMin": true } }
]
Das Beispiel „Bis“ an „Von“ binden steht ausführlich im Arbeitskalender, die übrigen Feldtypen unter Formulare.
Zeitzonen
| Ort | Zeitzone |
|---|---|
Server (Prozesse, Services, JSONata, $now()) |
UTC |
$$context.executedAt (Start, Benutzeraufgabe) |
ISO-Zeitstempel in UTC; $$context.finishedAt dagegen in Millisekunden seit 1970 |
| Browser (Formulare, Datensatz-Ansicht, Process Explorer) | Ortszeit der Person |
Timer-Start mit cron(…) |
Einstellung Zeitzone: Deutschland (Berlin), Österreich (Wien), Schweiz (Zürich) oder UTC (ohne Sommerzeit); andere IANA-Zonen werden akzeptiert. Prozesse ohne Zeitzone laufen in UTC. |
Timer-Start mit rate(…) |
ohne Zeitzone, Abstand ab dem Deploy |
| Zeitlimit einer Aufgabe mit Datum aus Prozessdaten | ein reines Datum gilt bis 23:59:59 UTC, also bis 00:59 bzw. 01:59 Uhr des Folgetags in Deutschland |
| Datenanalyse (Athena) | Zeitpunkte als timestamp in UTC, Daten als date |
| Arbeitskalender | nimmt ein Datum „wie geschrieben“: 2026-06-14T22:00:00Z zählt als 14.06., obwohl es in Berlin schon der 15.06. ist |
Wer eine Uhrzeit für Menschen ausgeben will, rechnet beim Formatieren um: in Handlebars mit {{dateTimeFormat datum "de-DE" "Europe/Berlin"}}, im Service Datum kalkulieren mit locale und timezone.
Funktionen
$fn('date_…') in JSONata und Platzhaltern
Diese Funktionen gelten in Input- und Output-Ausdrücken (JSONata) ebenso wie in Textfeldern mit Platzhaltern, siehe Template-Funktionen.
| Funktion | Ergebnis | Beispiel |
|---|---|---|
date_toGermanDate(datum) |
TT.MM.JJJJ; erwartet JJJJ-MM-TT ohne Uhrzeit |
$fn('date_toGermanDate', antrag_db.von) → 15.03.2026 |
date_toPath(datum) |
JJJJ/MM/TT aus einem Datum oder ISO-Zeitstempel; anderes bleibt unverändert |
$fn('date_toPath', $now()) → 2026/11/11 |
date_workdays(von, bis, arbeitstage?, halbStart?, halbEnde?) |
Arbeitstage nach dem Arbeitskalender, höchstens 3 Jahre | $fn('date_workdays', antrag.von, antrag.bis) → 4.5 |
date_workdaysByYear(…) |
Arbeitstage je Jahr | { "2026": 4, "2027": 4 } |
date_isWorkday(datum, arbeitstage?) |
true oder false |
$fn('date_isWorkday', $substring($now(), 0, 10)) |
date_freeDays(von, bis) |
freie Tage als Liste [{ date, name, fraction }] |
$fn('date_freeDays', antrag.von, antrag.bis) |
date_toGermanDate zerlegt den Text an den Bindestrichen. Ein Zeitstempel wird deshalb zuerst auf das Datum gekürzt: $fn('date_toGermanDate', $substring(prio_created_at, 0, 10)). Die Kalenderfunktionen kürzen Zeitstempel selbst; sie melden ungültige Daten („Ungültiges Datum für „Von“: … (erwartet JJJJ-MM-TT)“) und einen Zeitraum über drei Jahre als Fehler, der Schritt scheitert dann.
JSONata-Standard
| Funktion | Ergebnis | Beispiel |
|---|---|---|
$now() |
aktueller Zeitpunkt als ISO-Zeitstempel in UTC | 2026-11-11T09:30:00.000Z |
$substring($now(), 0, 10) |
heutiges Datum als JJJJ-MM-TT |
2026-11-11 |
$millis() |
aktueller Zeitpunkt in Millisekunden | 1794475800000 |
$toMillis(text) |
ISO-Datum oder -Zeitstempel in Millisekunden | $toMillis(rechnung.datum) |
$fromMillis(ms, bild?) |
Millisekunden als ISO-Zeitstempel oder nach einem Bild formatiert | $fromMillis($toMillis(datum) + 14 * 86400000, '[Y0001]-[M01]-[D01]') → 2026-03-29 |
| Vergleich | ISO-Texte lassen sich direkt vergleichen | rechnungsdatum <= $substring($now(), 0, 10) |
Die Bilder von $fromMillis folgen der JSONata-Dokumentation, zum Beispiel [D01].[M01].[Y0001] für 15.03.2026; Zeitzonen kennen sie nicht, das Ergebnis ist UTC. Tage werden als Millisekunden addiert: 86400000 ist ein Tag. Mehr unter JSONata in Pantarey.
Handlebars in PDF- und E-Mail-Vorlagen
| Helfer | Beispiel | Ergebnis |
|---|---|---|
dateTimeFormat |
{{dateTimeFormat erstellt "de-DE" "Europe/Berlin"}} |
11.11.2026, 10:30:00; alle drei Argumente nötig, sonst „(Fehlerhaftes Datum angegeben)“ |
dateTimeFormatWithSettings |
{{dateTimeFormatWithSettings erstellt "de-DE" "Europe/Berlin"}} |
11.11.2026, 10:30; ein Optionen-Objekt überschreibt die Vorgaben |
toGermanDate |
{{toGermanDate rechnungsdatum}} |
15.03.2026; nimmt die ersten zehn Zeichen, Zeitstempel sind erlaubt, keine Zeitzonen-Umrechnung |
currentDate |
{{currentDate "de-DE" "Europe/Berlin"}} |
11.11.2026 |
currentDateTime |
{{currentDateTime "de-DE" "Europe/Berlin"}} |
11.11.2026, 10:30:00 |
addDaysToCurrentDate |
{{addDaysToCurrentDate 14}} |
25.11.2026 (heute plus Tage, Europe/Berlin) |
month_name |
{{month_name rechnungsdatum}} |
März |
year |
{{year rechnungsdatum}} |
2026 |
sortBy sortiert Texte alphabetisch: ISO-Daten landen damit in der richtigen Reihenfolge, deutsche Daten TT.MM.JJJJ nicht. Alle Helfer unter Handlebars-Helfer.
Service „Datum kalkulieren“
Der Service Datum kalkulieren rechnet mit Zeitpunkten in einem eigenen Schritt. operation ist Pflicht, Eingaben sind ISO-Texte oder Millisekunden.
| Operation | Eingabe | Ergebnis |
|---|---|---|
add |
date, amount (negativ zieht ab), unit von millisecond bis year |
{ "result": "<ISO>", "timestamp": ms } |
difference |
date, date2, unit (Standard day) |
{ "difference": 14, "differenceExact": 14.25, "differenceMs": … }; bei month und year zählen nur Monats- und Jahreszahl |
format |
date, format oder locale mit timezone |
{ "formatted": "15.03.2026", "iso": …, "timestamp": … }; mit locale wird in die Zeitzone umgerechnet |
now |
– | aktueller Zeitpunkt mit Einzelteilen (components, dayOfWeek 0 = Sonntag) in UTC |
startOf, endOf |
date, unit |
Anfang oder Ende der Einheit in UTC; Woche Montag bis Sonntag |
Für eine Frist in Tagen reicht meist der JSONata-Einzeiler mit $fromMillis; der Service ist die Wahl für Monate, Jahre, Differenzen und startOf/endOf.
Arbeitskalender
Der Arbeitskalender des Kontos kennt Feiertage des Bundeslandes, örtliche Feiertage und freie Tage der Firma. Darauf bauen date_workdays, date_workdaysByYear, date_isWorkday und date_freeDays auf, das Formularfeld workday-count („Arbeitstage“, „ohne Feiertage und freie Tage der Firma“) zeigt die Tage schon beim Ausfüllen, und ein verbundener KI-Assistent zählt mit count_workdays. Ohne Einstellungen gelten die bundesweiten Feiertage und Montag bis Freitag.
Timer und Fristen
| Element | Einstellung | Verhalten |
|---|---|---|
| Timer-Start (Zeitplan) | Zeitplan rate(1 hour) oder cron(Minute Stunde Tag Monat Wochentag Jahr), Zeitzone |
cron braucht sechs Felder; Tag oder Wochentag ist ?. L steht für den letzten Tag im Monat, MON#1 für den ersten Montag; Wochentage auch als Zahl 1–7 mit 1 = Sonntag. Beispiele: cron(0 9 ? * MON-FRI *) werktags 9 Uhr, cron(0 9 1 1 ? *) am 1. Januar um 9 Uhr. Die Nachricht beginnt leer bis auf $$context. |
| Timer-Zwischenereignis (Warten) | Wartezeit (Sekunden), Standard 15, auch als Platzhalter $(wartezeit) |
nur ganze Sekunden über 0; 86400 ist ein Tag, 259200 drei Tage |
| Zeitlimit einer Benutzeraufgabe oder externen Aufgabe | Modus Standard: 30 Tage, Zeitspanne (Minuten, Stunden, Tage) oder Datum aus Prozessdaten mit $(frist); Verhalten bei Zeitüberschreitung Abbruch (Fehler) oder Weiter (automatisch fortfahren) |
Der Platzhalter liefert ein ISO-Datum oder eine Zahl in Sekunden. Ein reines Datum gilt bis 23:59:59 UTC; ein Datum in der Vergangenheit meldet „Frist bereits abgelaufen“, ein deutsches Datum „weder eine Zahl (Sekunden) noch ein gültiges ISO-Datum“. Bei Weiter steht danach $$context.timedOut auf true. |
Typische Fehler
| Fehler | Erklärung |
|---|---|
| Daten als Text vergleichen | ISO-Texte JJJJ-MM-TT und Zeitstempel sind alphabetisch gleich chronologisch, rechnungsdatum <= $substring($now(), 0, 10) stimmt. Deutsche Daten TT.MM.JJJJ sind es nicht: 01.04.2026 steht vor 15.03.2026. In SQL wird eine date-Spalte nicht mit einem Text verglichen, sondern mit einem Datumsliteral wie DATE '2026-01-01'. |
Zeitstempel in date_toGermanDate |
liefert ein falsches Ergebnis; zuerst mit $substring(wert, 0, 10) kürzen. Der Handlebars-Helfer toGermanDate kürzt selbst. |
| Zeitstempel im Arbeitskalender | wird auf das Datum gekürzt, ohne in die Ortszeit umzurechnen; ein Zeitpunkt um 23 Uhr UTC zählt zum UTC-Tag. Daten als JJJJ-MM-TT übergeben. |
| Sommerzeit | Ein Timer-Start mit Zeitzone UTC läuft im Sommer eine Stunde später in Deutschland als im Winter. Mit Deutschland (Berlin) bleibt 9 Uhr das ganze Jahr 9 Uhr. |
| Monatsende | add mit unit: month rechnet mit Kalendermonaten: 31. Januar plus ein Monat fällt in den März. difference mit month zählt nur Monatsnummern, der Tag spielt keine Rolle. Für den letzten Tag im Monat im Timer-Start L nutzen. |
| Millisekunden und ISO gemischt | $$context.executedAt ist ISO, $$context.finishedAt sind Millisekunden. $fromMillis und $toMillis wandeln um; der Service Datum kalkulieren nimmt beides. |
| Deutsche Daten in Fristen | Datum aus Prozessdaten und die Kalenderfunktionen verlangen JJJJ-MM-TT; der CSV-Import wandelt TT.MM.JJJJ beim Einlesen um. |
Beispiel
Die Musterfirma GmbH berechnet beim Speichern einer Rechnung die Frist im Input des Datenbank-Elements:
"frist": $fromMillis($toMillis(rechnung.rechnungsdatum) + 14 * 86400000, '[Y0001]-[M01]-[D01]')ergibt aus2026-03-15den2026-03-29. Die Benutzeraufgabe „Zahlungseingang prüfen“ hat als Zeitlimit Datum aus Prozessdaten$(rechnung_db.frist)mit Weiter (automatisch fortfahren); ein Gateway prüft$(pruefung.$$context.timedOut) === true. Die Mahnung verschickt ein zweiter Prozess mit Timer-Startcron(0 9 ? * MON-FRI *)und Zeitzone Deutschland (Berlin): Sein Datenbank-Element liest die fälligen Rechnungen mitfrist < $substring($now(), 0, 10)und bekommt den Output-Ausdruck{ "rechnungen": $, "werktag": $fn('date_isWorkday', $substring($now(), 0, 10)) }mit Ergebnispfadfaellig. Das Gateway dahinter lässt mit$(faellig.werktag) === truenur an Arbeitstagen nach dem Arbeitskalender Mahnungen zu, an Feiertagen endet der Vorgang.
Häufige Fragen
Warum steht in der Datenbank eine andere Uhrzeit als eingegeben?
Zeitpunkte werden in UTC gespeichert und enden auf Z. Die Anzeige in Formularen und in der Datensatz-Ansicht rechnet in die Ortszeit zurück; in Rohdaten, SQL und E-Mail-Vorlagen erscheint UTC, dort mit dateTimeFormat und "Europe/Berlin" formatieren.
Wie wird ein Datum in deutscher Schreibweise ausgegeben?
In JSONata und Platzhaltern mit $fn('date_toGermanDate', datum) für JJJJ-MM-TT, bei Zeitstempeln vorher $substring(datum, 0, 10). In Vorlagen mit {{toGermanDate datum}} oder, mit Uhrzeit, {{dateTimeFormat datum "de-DE" "Europe/Berlin"}}.
Wie wird heute plus 14 Tage berechnet?
$fromMillis($millis() + 14 * 86400000, '[Y0001]-[M01]-[D01]') in JSONata oder der Service Datum kalkulieren mit operation: "add", amount: 14, unit: "day". In einer Vorlage {{addDaysToCurrentDate 14}}.
Warum startet der Timer eine Stunde zu spät?
Der Prozess hat keine Zeitzone und läuft in UTC. Zeitzone auf Deutschland (Berlin) setzen und neu deployen.
Gibt es ein Feld nur für eine Uhrzeit?
Nein. Eine Uhrzeit wird als Zeitpunkt mit Datum oder als Text gespeichert; eine Dauer als Zahl mit dem Format Dauer in Millisekunden.
Warum zählt der Arbeitskalender einen Tag zu wenig?
Übergeben wurde ein Zeitstempel kurz vor Mitternacht UTC; er zählt zum UTC-Tag. Daten als JJJJ-MM-TT übergeben, etwa aus einem Feld vom Typ Datum.
Nächster Schritt
Kochbuch: fertige Rezepte für Rechnungsnummern, Summen, Dateien, Status, Bedingungen und Fristen.