Skip to content

Date and time

Pantarey stores dates as text in ISO format and calculates on the server in UTC; the browser displays them in the person's local time. Whoever knows these two rules understands almost every date question: why a timestamp in the database ends in Z, why ISO dates can be compared as text and German dates cannot, and why a timer start needs a time zone. This page collects storage formats, time zones, all date functions and the time behaviour of timers and deadlines in one place.

Storage formats

In the Data Designer the group Date & time offers two types; there is no separate type for a time of day without a date.

Type Description Stored Example In the form
Date day, month, year YYYY-MM-DD (format date) 2026-03-15 calendar field (dateInput); default today, today+7, today-1
Timestamp date with time ISO timestamp in UTC with Z (format date-time) 2026-03-15T09:30:00.000Z date and time in local time (dateTimeInput); default now

A duration is a number with the number format Duration, stored in milliseconds. System fields such as the creation and modification time of a record are timestamps in the same ISO format.

For a timestamp the person enters local time; the conversion to UTC is stored. 10:30 in Augsburg in March becomes 09:30:00.000Z, in July 08:30:00.000Z. A date without time stays as entered.

Display

Input fields show date and time the way the browser does for its language; Pantarey sets no display format. The record view shows timestamps as DD.MM.YYYY HH:mm in the browser's time zone; the Process Explorer shows timestamps as DD.MM.YYYY, HH:mm:ss.

Date field bounds

In custom forms (Formly JSON) dateInput and dateTimeInput limit the allowed range; in the Data Designer these properties are not set.

Property (props) Effect
min, max fixed bound: YYYY-MM-DD, today, today+N, today-N; for a timestamp also now or a time of day
minKey, maxKey key of another date field on the same level (in a repeat: the same row); its value is the bound. If a fixed bound applies as well, the stricter one counts.
followMin: true if the field from minKey moves past this field's value, that value is adopted; an empty field stays empty

The calendar greys out days outside the bounds; a typed value outside reports "Must not be before 'From'.", "Must not be after 'To'.", "Earliest on 05.11.2026." or "Latest on 30.11.2026." (for a timestamp "Earliest 05.11.2026, 09:00."). The server checks the bounds again on submit.

[
  { "key": "from", "type": "dateInput", "props": { "label": "From", "required": true }, "defaultValue": "today" },
  { "key": "to", "type": "dateInput", "props": { "label": "To", "required": true, "minKey": "from", "followMin": true } }
]

The example of binding "To" to "From" is explained in the work calendar, the other field types under Forms.

Time zones

Place Time zone
Server (processes, services, JSONata, $now()) UTC
$$context.executedAt (start, user task) ISO timestamp in UTC; $$context.finishedAt, however, in milliseconds since 1970
Browser (forms, record view, Process Explorer) the person's local time
Timer start with cron(…) setting Time zone: Germany (Berlin), Austria (Vienna), Switzerland (Zurich) or UTC (no daylight saving); other IANA zones are accepted. Processes without a time zone run in UTC.
Timer start with rate(…) no time zone, interval counted from the deploy
Time limit of a task with Date from process data a plain date is valid until 23:59:59 UTC, i.e. until 00:59 or 01:59 of the following day in Germany
Data analytics (Athena) timestamps as timestamp in UTC, dates as date
Work calendar takes a date "as written": 2026-06-14T22:00:00Z counts as 14 June although it is already 15 June in Berlin

To output a time for people, convert when formatting: in Handlebars with {{dateTimeFormat date "de-DE" "Europe/Berlin"}}, in the service Date Calculation with locale and timezone.

Functions

$fn('date_…') in JSONata and placeholders

These functions work in input and output expressions (JSONata) as well as in text fields with placeholders, see Template functions.

Function Result Example
date_toGermanDate(date) DD.MM.YYYY; expects YYYY-MM-DD without time $fn('date_toGermanDate', antrag_db.von) → 15.03.2026
date_toPath(date) YYYY/MM/DD from a date or ISO timestamp; anything else stays unchanged $fn('date_toPath', $now()) → 2026/11/11
date_workdays(from, to, workdays?, halfStart?, halfEnd?) working days according to the work calendar, at most 3 years $fn('date_workdays', request.from, request.to) → 4.5
date_workdaysByYear(…) working days per year { "2026": 4, "2027": 4 }
date_isWorkday(date, workdays?) true or false $fn('date_isWorkday', $substring($now(), 0, 10))
date_freeDays(from, to) days off as a list [{ date, name, fraction }] $fn('date_freeDays', request.from, request.to)

date_toGermanDate splits the text at the hyphens. A timestamp is therefore shortened to the date first: $fn('date_toGermanDate', $substring(prio_created_at, 0, 10)). The calendar functions shorten timestamps themselves; they report invalid dates ("Ungültiges Datum für „Von“: … (erwartet JJJJ-MM-TT)") and a period of more than three years as an error, and the step then fails.

JSONata standard

Function Result Example
$now() current time as ISO timestamp in UTC 2026-11-11T09:30:00.000Z
$substring($now(), 0, 10) today's date as YYYY-MM-DD 2026-11-11
$millis() current time in milliseconds 1794475800000
$toMillis(text) ISO date or timestamp in milliseconds $toMillis(invoice.date)
$fromMillis(ms, picture?) milliseconds as ISO timestamp or formatted by a picture $fromMillis($toMillis(date) + 14 * 86400000, '[Y0001]-[M01]-[D01]') → 2026-03-29
Comparison ISO texts can be compared directly invoice_date <= $substring($now(), 0, 10)

The pictures of $fromMillis follow the JSONata documentation, for example [D01].[M01].[Y0001] for 15.03.2026; they know no time zones, the result is UTC. Days are added as milliseconds: 86400000 is one day. More under JSONata in Pantarey.

Handlebars in PDF and e-mail templates

Helper Example Result
dateTimeFormat {{dateTimeFormat created "de-DE" "Europe/Berlin"}} 11.11.2026, 10:30:00; all three arguments required, otherwise "(Fehlerhaftes Datum angegeben)"
dateTimeFormatWithSettings {{dateTimeFormatWithSettings created "de-DE" "Europe/Berlin"}} 11.11.2026, 10:30; an options object overrides the defaults
toGermanDate {{toGermanDate invoice_date}} 15.03.2026; takes the first ten characters, timestamps are allowed, no time zone conversion
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 (today plus days, Europe/Berlin)
month_name {{month_name invoice_date}} März (German month name)
year {{year invoice_date}} 2026

sortBy sorts texts alphabetically: ISO dates thus end up in the right order, German dates DD.MM.YYYY do not. All helpers under Handlebars helpers.

Service "Date Calculation"

The service Date Calculation calculates with timestamps in a step of its own. operation is required, inputs are ISO texts or milliseconds.

Operation Input Result
add date, amount (negative subtracts), unit from millisecond to year { "result": "<ISO>", "timestamp": ms }
difference date, date2, unit (default day) { "difference": 14, "differenceExact": 14.25, "differenceMs": … }; for month and year only the month and year numbers count
format date, format or locale with timezone { "formatted": "15.03.2026", "iso": …, "timestamp": … }; with locale the value is converted into the time zone
now – current time with its parts (components, dayOfWeek 0 = Sunday) in UTC
startOf, endOf date, unit start or end of the unit in UTC; week Monday to Sunday

For a deadline in days the JSONata one-liner with $fromMillis is usually enough; the service is the choice for months, years, differences and startOf/endOf.

Work calendar

The work calendar of the account knows the public holidays of the federal state, local holidays and the company's days off. date_workdays, date_workdaysByYear, date_isWorkday and date_freeDays build on it, the form field workday-count ("Working days", "excluding public holidays and company days off") shows the days while filling in the form, and a connected AI assistant counts with count_workdays. Without settings, the nationwide public holidays and Monday to Friday apply.

Timers and deadlines

Element Setting Behaviour
Timer start (Schedule) Schedule rate(1 hour) or cron(minute hour day month weekday year), Time zone cron needs six fields; day or weekday is ?. L stands for the last day of the month, MON#1 for the first Monday; weekdays also as numbers 1–7 with 1 = Sunday. Examples: cron(0 9 ? * MON-FRI *) weekdays at 9 am, cron(0 9 1 1 ? *) on 1 January at 9 am. The message starts empty except for $$context.
Timer intermediate event (Waiting) Waiting time (seconds), default 15, also as placeholder $(waiting_time) whole seconds above 0 only; 86400 is one day, 259200 three days
Time limit of a user task or external task Mode Default: 30 days, Duration (minutes, hours, days) or Date from process data with $(deadline); Behaviour on timeout Cancel (error) or Continue (proceed automatically) The placeholder returns an ISO date or a number of seconds. A plain date is valid until 23:59:59 UTC; a date in the past reports "Frist bereits abgelaufen" (deadline already passed), a German date "weder eine Zahl (Sekunden) noch ein gültiges ISO-Datum" (neither a number of seconds nor a valid ISO date). With Continue, $$context.timedOut is true afterwards.

Typical errors

Error Explanation
Comparing dates as text ISO texts YYYY-MM-DD and timestamps are chronological when sorted alphabetically, invoice_date <= $substring($now(), 0, 10) is correct. German dates DD.MM.YYYY are not: 01.04.2026 comes before 15.03.2026. In SQL a date column is not compared with a text but with a date literal such as DATE '2026-01-01'.
Timestamp in date_toGermanDate returns a wrong result; shorten first with $substring(value, 0, 10). The Handlebars helper toGermanDate shortens by itself.
Timestamp in the work calendar is shortened to the date without converting to local time; a time at 11 pm UTC counts for the UTC day. Pass dates as YYYY-MM-DD.
Daylight saving time A timer start with time zone UTC runs one hour later in Germany in summer than in winter. With Germany (Berlin), 9 am stays 9 am all year.
End of month add with unit: month calculates with calendar months: 31 January plus one month falls into March. difference with month counts month numbers only, the day is ignored. For the last day of the month in a timer start use L.
Milliseconds and ISO mixed $$context.executedAt is ISO, $$context.finishedAt is milliseconds. $fromMillis and $toMillis convert; the service Date Calculation accepts both.
German dates in deadlines Date from process data and the calendar functions require YYYY-MM-DD; the CSV import converts DD.MM.YYYY when reading.

Example

Musterfirma GmbH calculates the deadline when saving an invoice, in the input of the database element: "deadline": $fromMillis($toMillis(invoice.invoice_date) + 14 * 86400000, '[Y0001]-[M01]-[D01]') turns 2026-03-15 into 2026-03-29. The user task "Check payment receipt" has the Time limit Date from process data $(invoice_db.deadline) with Continue (proceed automatically); a gateway checks $(check.$$context.timedOut) === true. The reminder is sent by a second process with the timer start cron(0 9 ? * MON-FRI *) and Time zone Germany (Berlin): its database element reads the due invoices with deadline < $substring($now(), 0, 10) and gets the output expression { "invoices": $, "workday": $fn('date_isWorkday', $substring($now(), 0, 10)) } with result path due. The gateway after it only lets reminders through on working days according to the work calendar with $(due.workday) === true; on public holidays the instance ends.

Frequently asked questions

Why does the database show a different time than entered?

Timestamps are stored in UTC and end in Z. The display in forms and in the record view converts back to local time; in raw data, SQL and e-mail templates UTC appears, so format there with dateTimeFormat and "Europe/Berlin".

How is a date output in German notation?

In JSONata and placeholders with $fn('date_toGermanDate', date) for YYYY-MM-DD, for timestamps $substring(date, 0, 10) first. In templates with {{toGermanDate date}} or, with time, {{dateTimeFormat date "de-DE" "Europe/Berlin"}}.

How is today plus 14 days calculated?

$fromMillis($millis() + 14 * 86400000, '[Y0001]-[M01]-[D01]') in JSONata or the service Date Calculation with operation: "add", amount: 14, unit: "day". In a template {{addDaysToCurrentDate 14}}.

Why does the timer start one hour late?

The process has no time zone and runs in UTC. Set Time zone to Germany (Berlin) and deploy again.

Is there a field for a time of day only?

No. A time of day is stored as a Timestamp with a date or as text; a duration as a number with the format Duration in milliseconds.

Why does the work calendar count one day too few?

A timestamp shortly before midnight UTC was passed; it counts for the UTC day. Pass dates as YYYY-MM-DD, for example from a field of type Date.

Next step

Cookbook: ready-made recipes for invoice numbers, sums, files, status, conditions and deadlines.