From 4251f56a4deca276a6ff46acebbb8a46951ff946 Mon Sep 17 00:00:00 2001 From: ybai08 <271969568+ybai08@users.noreply.github.com> Date: Sat, 17 Oct 2026 12:44:58 -0500 Subject: [PATCH] Return :error when casting malformed datetimes without seconds Strings such as "2015-01-23 ab:50" matched the YYYY-MM-DD HH:MM clause of the :naive_datetime and :utc_datetime casts, and the nil parts produced by to_i/1 were passed to NaiveDateTime.new/6, which raised a FunctionClauseError instead of returning :error. Build the date and time through the guarded cast_date/3 and cast_time/4 helpers instead. --- lib/ecto/type.ex | 12 ++++++------ test/ecto/type_test.exs | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/ecto/type.ex b/lib/ecto/type.ex index 243d94dad8..a5b0627fa3 100644 --- a/lib/ecto/type.ex +++ b/lib/ecto/type.ex @@ -1179,9 +1179,9 @@ defmodule Ecto.Type do minute::2-bytes>> ) when sep in [?\s, ?T] do - case NaiveDateTime.new(to_i(year), to_i(month), to_i(day), to_i(hour), to_i(minute), 0) do - {:ok, _} = ok -> ok - _ -> :error + with {:ok, date} <- cast_date(to_i(year), to_i(month), to_i(day)), + {:ok, time} <- cast_time(to_i(hour), to_i(minute), 0, nil) do + NaiveDateTime.new(date, time) end end @@ -1232,9 +1232,9 @@ defmodule Ecto.Type do minute::2-bytes>> ) when sep in [?\s, ?T] do - case NaiveDateTime.new(to_i(year), to_i(month), to_i(day), to_i(hour), to_i(minute), 0) do - {:ok, naive_datetime} -> {:ok, DateTime.from_naive!(naive_datetime, "Etc/UTC")} - _ -> :error + with {:ok, date} <- cast_date(to_i(year), to_i(month), to_i(day)), + {:ok, time} <- cast_time(to_i(hour), to_i(minute), 0, nil) do + DateTime.new(date, time, "Etc/UTC") end end diff --git a/test/ecto/type_test.exs b/test/ecto/type_test.exs index 7174a20e2f..06743295ad 100644 --- a/test/ecto/type_test.exs +++ b/test/ecto/type_test.exs @@ -657,6 +657,9 @@ defmodule Ecto.TypeTest do assert Ecto.Type.cast(:naive_datetime, "2015-01-23P23:50:07") == :error assert Ecto.Type.cast(:naive_datetime, "2015-01-23 23:50.123") == :error assert Ecto.Type.cast(:naive_datetime, "2015-01-23 23:50Z") == :error + assert Ecto.Type.cast(:naive_datetime, "abcd-01-23 23:50") == :error + assert Ecto.Type.cast(:naive_datetime, "2015-01-23 ab:50") == :error + assert Ecto.Type.cast(:naive_datetime, "2015-01-23T23:5x") == :error assert Ecto.Type.cast(:naive_datetime, %{ "year" => "2015", @@ -1013,6 +1016,9 @@ defmodule Ecto.TypeTest do assert Ecto.Type.cast(:utc_datetime, "2015-01-23P23:50:07") == :error assert Ecto.Type.cast(:utc_datetime, "2015-01-23 23:50.123") == :error assert Ecto.Type.cast(:utc_datetime, "2015-01-23 23:50Z") == :error + assert Ecto.Type.cast(:utc_datetime, "abcd-01-23 23:50") == :error + assert Ecto.Type.cast(:utc_datetime, "2015-01-23 ab:50") == :error + assert Ecto.Type.cast(:utc_datetime, "2015-01-23T23:5x") == :error assert Ecto.Type.cast(:utc_datetime, %{ "year" => "2015",