diff --git a/CHANGELOG.md b/CHANGELOG.md index 8abd9341bd..c3b0e76bff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * [Ecto.Query] Fix subqueries in interpolated join filters (this changes the representation of `JoinExpr.on` fields to a `BooleanExpr`) * [Ecto.Query] Fix selecting lists of fields and using `map/2` from subqueries + * [Ecto.Type] Normalize `DateTime` casts to UTC for `:time` and `:time_usec` ## v3.14.2 (2026-08-14) diff --git a/lib/ecto/type.ex b/lib/ecto/type.ex index f697dc562f..107cafce71 100644 --- a/lib/ecto/type.ex +++ b/lib/ecto/type.ex @@ -1114,6 +1114,13 @@ defmodule Ecto.Type do end end + defp cast_time(%DateTime{} = datetime) do + case cast_utc_datetime(datetime) do + {:ok, datetime} -> {:ok, DateTime.to_time(datetime)} + :error -> :error + end + end + defp cast_time(%{"hour" => empty, "minute" => empty}) when empty in ["", nil], do: {:ok, nil} diff --git a/test/ecto/type_test.exs b/test/ecto/type_test.exs index f2bc5aafe3..b84aa03961 100644 --- a/test/ecto/type_test.exs +++ b/test/ecto/type_test.exs @@ -532,6 +532,9 @@ defmodule Ecto.TypeTest do assert Ecto.Type.cast(:time, ~D[2016-11-11]) == :error + + assert Ecto.Type.cast(:time, @non_utc_datetime) == + {:ok, ~T[22:30:07]} end test "dump" do @@ -607,6 +610,8 @@ defmodule Ecto.TypeTest do test "cast from DateTime" do utc_datetime = DateTime.from_naive!(~N[2016-11-11 23:30:10], "Etc/UTC") assert Ecto.Type.cast(:time_usec, utc_datetime) == {:ok, ~T[23:30:10.000000]} + + assert Ecto.Type.cast(:time_usec, @non_utc_datetime) == {:ok, ~T[22:30:07.008000]} end test "cast from Date" do