Timestamps¶
Every SDK method that takes a timestamp requires a tz-aware
datetime. Naive datetimes raise ValueError at the call site —
never silently converted, never assumed UTC.
Constructing one¶
from datetime import datetime, timezone
start = datetime(2024, 1, 1, tzinfo=timezone.utc)
end = datetime(2024, 1, 2, tzinfo=timezone.utc)
For non-UTC zones:
from datetime import datetime
from zoneinfo import ZoneInfo
start = datetime(2024, 1, 1, 8, 0, tzinfo=ZoneInfo("America/Los_Angeles"))
The SDK serializes to UTC ISO-8601 internally regardless of the input zone.
Wire formats — two flavors¶
The server is inconsistent about timestamp formats across endpoints (RESEARCH §2 documents which use which):
- Alarms / events / positions — Unix milliseconds.
- Reports — ISO-8601 strings.
The SDK picks the right format per endpoint. You always pass the same
Python datetime; the SDK handles the conversion.
Parsing server responses¶
Returned model fields use datetime for any timestamp. They are
tz-aware (UTC). Compare them with your own tz-aware values:
for assoc in client.tag_associations.list_active_in_period(start, end):
if assoc.opened_at > start:
...
Anti-patterns¶
# WRONG — naive datetime raises ValueError
start = datetime(2024, 1, 1)
client.alarms.list_for_site(start, end, site_uid="s-1")
# WRONG — int seconds; the SDK only accepts datetime
client.alarms.list_for_site(1704067200, end, site_uid="s-1")
These are intentional. Naive datetimes are a frequent source of silent off-by-N-hour bugs; the SDK refuses to guess.