Skip to content

Commit 24f914a

Browse files
authored
Normalize DateTime casts to UTC for :time and :time_usec (#4792)
#4775 moved :date, :naive_datetime and :naive_datetime_usec onto the UTC instant when given a non-UTC %DateTime{}, but cast_time/1 has no %DateTime{} clause, so it falls through to the map clauses and rebuilds a Time from the wall-clock fields.
1 parent e1d536a commit 24f914a

3 files changed

Lines changed: 13 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
* [Ecto.Query] Fix subqueries in interpolated join filters (this changes the representation of `JoinExpr.on` fields to a `BooleanExpr`)
1313
* [Ecto.Query] Fix selecting lists of fields and using `map/2` from subqueries
14+
* [Ecto.Type] Normalize `DateTime` casts to UTC for `:time` and `:time_usec`
1415

1516
## v3.14.2 (2026-08-14)
1617

‎lib/ecto/type.ex‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,13 @@ defmodule Ecto.Type do
11141114
end
11151115
end
11161116

1117+
defp cast_time(%DateTime{} = datetime) do
1118+
case cast_utc_datetime(datetime) do
1119+
{:ok, datetime} -> {:ok, DateTime.to_time(datetime)}
1120+
:error -> :error
1121+
end
1122+
end
1123+
11171124
defp cast_time(%{"hour" => empty, "minute" => empty}) when empty in ["", nil],
11181125
do: {:ok, nil}
11191126

‎test/ecto/type_test.exs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,9 @@ defmodule Ecto.TypeTest do
532532

533533
assert Ecto.Type.cast(:time, ~D[2016-11-11]) ==
534534
:error
535+
536+
assert Ecto.Type.cast(:time, @non_utc_datetime) ==
537+
{:ok, ~T[22:30:07]}
535538
end
536539

537540
test "dump" do
@@ -607,6 +610,8 @@ defmodule Ecto.TypeTest do
607610
test "cast from DateTime" do
608611
utc_datetime = DateTime.from_naive!(~N[2016-11-11 23:30:10], "Etc/UTC")
609612
assert Ecto.Type.cast(:time_usec, utc_datetime) == {:ok, ~T[23:30:10.000000]}
613+
614+
assert Ecto.Type.cast(:time_usec, @non_utc_datetime) == {:ok, ~T[22:30:07.008000]}
610615
end
611616

612617
test "cast from Date" do

0 commit comments

Comments
 (0)