Elixir version
any
Database and Version
any
Ecto Versions
3.14.1
Database Adapter and Versions (postgrex, myxql, etc)
any
Current behavior
Ecto.Type.cast/2 has no explicit %DateTime{} handling for :naive_datetime, :naive_datetime_usec, or :date. These values fall through to the generic map handling which reconstructs a value from its wall-clock fields and implicitly discard utc_offset, std_offset, and time_zone.
Wall time preservation is consistent with DateTime.to_naive/1 but here it's undocumented and seems accidental. It is inconsistent with other ecto paths that either normalize or reject non-UTC.
Repro:
berlin = %DateTime{
calendar: Calendar.ISO,
year: 2020,
month: 6,
day: 1,
hour: 0,
minute: 30,
second: 7,
microsecond: {8000, 6},
utc_offset: 3600,
std_offset: 3600,
time_zone: "Europe/Berlin",
zone_abbr: "CEST"
}
Ecto.Type.cast(:naive_datetime, berlin)
# => {:ok, ~N[2020-06-01 00:30:07]}
Ecto.Type.cast(:naive_datetime_usec, berlin)
# => {:ok, ~N[2020-06-01 00:30:07.008000]}
Ecto.Type.cast(:date, berlin)
# => {:ok, ~D[2020-06-01]}
Ecto.Type.cast(:utc_datetime_usec, berlin)
# => {:ok, ~U[2020-05-31 22:30:07.008000Z]}
Other Ecto datetime paths establish stronger UTC expectations:
- Casting a non-UTC DateTime to
:utc_datetime normalizes it to UTC.
- Loading a
DateTime as a naive datetime requires its timezone to be "Etc/UTC".
- Dumping a
DateTime directly as a naive datetime is rejected.
Expected behavior
Three solutions possible:
- Normalize to UTC before removing timezone information
- Reject non-UTC DateTime values, consistent with the stricter load/dump paths
- Preserve local wall time intentionally, but add explicit
%DateTime{} clauses, documentation, and tests establishing that contract
Elixir version
any
Database and Version
any
Ecto Versions
3.14.1
Database Adapter and Versions (postgrex, myxql, etc)
any
Current behavior
Ecto.Type.cast/2has no explicit%DateTime{}handling for:naive_datetime,:naive_datetime_usec, or:date. These values fall through to the generic map handling which reconstructs a value from its wall-clock fields and implicitly discardutc_offset,std_offset, andtime_zone.Wall time preservation is consistent with
DateTime.to_naive/1but here it's undocumented and seems accidental. It is inconsistent with other ecto paths that either normalize or reject non-UTC.Repro:
Other Ecto datetime paths establish stronger UTC expectations:
:utc_datetimenormalizes it to UTC.DateTimeas a naive datetime requires its timezone to be "Etc/UTC".DateTimedirectly as a naive datetime is rejected.Expected behavior
Three solutions possible:
%DateTime{}clauses, documentation, and tests establishing that contract