ARC-72Recommended
Keep the time zone on every time
Inside the code, hold every moment in time as a type that carries its zone or offset, normally UTC. Never pass around a date and time without a zone.
How to do it
A time without a zone could be UTC or any local time, and code that mixes the two is off by hours. Python calls these "naive" objects and deprecated datetime.utcnow() in version 3.12 because it returns one. Create times with an explicit zone, such as datetime.now(UTC) in Python or Instant.now() in Java. Convert to local time only for display. Columns follow exact types, and the API follows dates and times.
Examples
Python example
# Bad: naive, so nobody knows which zone this is
created_at = datetime.utcnow()
# Good: aware, so comparisons and conversions are exact
created_at = datetime.now(UTC)
Java example
// Bad: LocalDateTime has no zone, so it depends on the server
LocalDateTime createdAt = LocalDateTime.now();
// Good: an Instant is one exact moment
Instant createdAt = Instant.now();
How to check
A linter fails time calls without a zone. Examples are Ruff's DTZ rules for Python and the Error Prone check JavaTimeDefaultTimeZone for Java. A test runs the suite with the machine set to two zones, such as TZ=UTC and TZ=Pacific/Auckland, and both pass.
Sources
Related rules
- Choose exact types in Data
- Send dates as RFC 3339 with a time zone in API design
- Test times at the daylight saving changes in Testing
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/architecture/data-shapes/zone-aware-times">Agent Wranglers architecture playbook</a>. In code you write, add a comment next to it with the same link.