Dates and Times

How the API returns dates and times, which formats it accepts in requests, and how fields that only hold a date are represented.

The API returns every date and time in UTC.
In requests, it accepts UTC as well as local time in the Europe/Berlin time zone.

📘

In short

Read every value as UTC.
Send UTC with a trailing Z, e.g. 2025-03-10T15:09:36Z.

Responses

Every date and time in a response is an ISO 8601 timestamp in UTC, with whole seconds and a trailing Z:

{
    "created": "2025-03-10T15:09:36Z",
    "lastModified": null
}

The format is always yyyy-MM-ddTHH:mm:ssZ.
A value that isn't set is null.

Webhooks

Dates and times in webhook messages are in UTC with a trailing Z as well, but they may contain fractional seconds, e.g. 2026-01-15T08:05:37.873Z.
Parse them as ISO 8601 instead of relying on a fixed number of digits.

Requests

Dates and times in request bodies, including the filters of search requests, are read as follows:

FormatExampleRead as
UTC with a trailing Z2025-03-10T15:09:36ZUTC. Recommended.
Without a time zone2025-03-10T16:09:36Local time in Europe/Berlin (CET in winter, CEST in summer).
Date only2025-03-10Midnight of that day in Europe/Berlin.
With an offset2025-03-10T16:09:36+01:00Not supported. The offset isn't applied correctly, so the value may be off by hours.

The first two examples describe the same point in time.
Fractional seconds, such as 2025-03-10T15:09:36.500Z, are accepted.

A value that can't be read is rejected with 400 Bad Request and the message Could not read datetime.
Always use ISO 8601, as other formats aren't guaranteed to be read correctly.

Fields that only hold a date

Some fields only hold a calendar date without a time, for example the move-in and move-out dates of a unit contract.
facilioo stores such a date as midnight in Europe/Berlin and returns that point in time in UTC, like any other value.

Stored dateReturned value
10 March 2025 (CET, UTC+1)2025-03-09T23:00:00Z
1 May 2025 (CEST, UTC+2)2025-04-30T22:00:00Z

The returned value therefore shows the previous day in UTC.
To get the date, convert the value to Europe/Berlin and use its date part.
When writing such a field, send the date only, e.g. 2025-05-01.

Filtering by date

Range filters such as createdFrom and createdUntil include their bounds: an entity created exactly at createdFrom is part of the result.
Send the bounds in UTC to avoid the shift between UTC and Europe/Berlin:

{
    "createdFrom": "2025-01-01T00:00:00Z",
    "createdUntil": "2025-12-31T23:59:59Z"
}

Filtering fields that only hold a date

To filter a field that only holds a date, send the date only, e.g. 2025-03-10.
It is read as midnight in Europe/Berlin, which is exactly the point in time facilioo stores for that date, so you don't need to convert anything to UTC.
To find all consumption meters whose next reading is due on 10 March 2025, send the same date as both bounds:

{
    "nextReadingDateAfter": "2025-03-10",
    "nextReadingDateBefore": "2025-03-10"
}

This matches the consumption meters that the API returns with "nextReadingDate": "2025-03-09T23:00:00Z".
Don't send the UTC midnight of the date, e.g. 2025-03-10T00:00:00Z, as that is 1 am in Europe/Berlin and misses the stored value.