Custom Ecto.Type for regional time zone conversion with UTC storage
0 reputation · 21 Dec 2025, 15:08 UTC
An Elixir application using Ecto with PostgreSQL needs to store all timestamps as UTC via :utc_datetime / timestamptz while accepting and presenting values in a per-user regional time zone. tzdata is available for Calendar.DateTime.shift_zone/3.
Ecto does not perform automatic regional conversion. The documented pattern is UTC storage in the database with conversion at the application boundary or via a custom Ecto.Type implementing init/1, load/1, dump/1 and cast/1. :naive_datetime maps to timestamp without time zone and leaves zone interpretation to the app, which raises ambiguity around DST transitions.
The design decision is where and how to encapsulate conversion without breaking :utc_datetime compatibility and tzdata versioning.
- Which layer is appropriate for consistent zone handling across schemas, application boundary versus a reusable custom Ecto.Type?
- Does a custom type that converts in dump/1 and load/1 need to guarantee UTC DateTime structs for Postgrex encoding to remain compatible with :utc_datetime?
- What behavior should cast/1 have for ISO strings with offsets versus naive strings when the target storage is UTC?