`no case clause matching` error when inserting utc_datetime into db?

I recently updated a few :date column types in one table to be :utc_datetime, and have added a helper method to add hours + minutes to those fields in order to make them convertible to a :utc_datetime if they come in with only MMDDYY set. It gets through the type casting / validations, but fails when it’s time to insert into the db (Repo.insert(changeset)), with this error:

** (CaseClauseError) no case clause matching: {{{2010, 4, 17}, {0, 0, 0, 0}}}

Here are the relevant items from the model model:

  schema "campaigns" do
    field :start_date, :utc_datetime
    field :end_date, :utc_datetime
    field :due_date, :utc_datetime
    field :original_copy, :string

    timestamps(type: :utc_datetime)

  @doc """
  Builds a changeset based on the `struct` and `params`.
  def changeset(struct, params \\ %{}) do
    |> cast(utc_compatible_dates(params), [:start_date, :end_date, :due_date, :original_copy])
    |> validate_required([:start_date, :end_date, :due_date, :original_copy])

  defp utc_compatible_dates(params) do
    midnight = %{hour: 0, minute: 0}

    try do
      Map.update!(params, :start_date, &Map.merge(midnight, &1))
      |> Map.update!(:end_date, &Map.merge(midnight, &1))
      |> Map.update!(:due_date, &Map.merge(midnight, &1))
      # if one of these is missing, let the requirement validation catch it
      KeyError -> params

here’s what happens, with full stacktrace:

attrs = %{ original_copy: "some content", end_date: %{day: 17, month: 4, year: 2010}, start_date: %{day: 17, month: 4, year: 2010}, due_date: %{day: 17, month: 4, year: 2010} }

changeset = Campaign.changeset(%Campaign{}, attrs)
#=>Ecto.Changeset<action: nil,
 changes: %{due_date: #<DateTime(2010-04-17T00:00:00Z Etc/UTC)>,
   end_date: #<DateTime(2010-04-17T00:00:00Z Etc/UTC)>,
   original_copy: "some content", start_date: #<DateTime(2010-04-17T00:00:00Z Etc/UTC)>},
   errors: [], data: #Jingle.Campaign<>, valid?: true>

iex(5)> Repo.insert(changeset)

** (CaseClauseError) no case clause matching: {{{2010, 4, 17}, {0, 0, 0, 0}}}
             (ecto) lib/ecto/adapters/postgres/datetime.ex:40: Ecto.Adapters.Postgres.TypeModule.encode_params/3
         (postgrex) lib/postgrex/query.ex:45: DBConnection.Query.Postgrex.Query.encode/3
    (db_connection) lib/db_connection.ex:1071: DBConnection.describe_run/5
    (db_connection) lib/db_connection.ex:1142: anonymous fn/4 in DBConnection.run_meter/5
    (db_connection) lib/db_connection.ex:584: DBConnection.prepare_execute/4
             (ecto) lib/ecto/adapters/postgres/connection.ex:93: Ecto.Adapters.Postgres.Connection.execute/4
             (ecto) lib/ecto/adapters/sql.ex:243: Ecto.Adapters.SQL.sql_call/6
             (ecto) lib/ecto/adapters/sql.ex:562: Ecto.Adapters.SQL.struct/7
             (ecto) lib/ecto/repo/schema.ex:467: Ecto.Repo.Schema.apply/4
             (ecto) lib/ecto/repo/schema.ex:205: anonymous fn/13 in Ecto.Repo.Schema.do_insert/4
             (ecto) lib/ecto/repo/schema.ex:684: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
             (ecto) lib/ecto/adapters/sql.ex:620: anonymous fn/3 in Ecto.Adapters.SQL.do_transaction/3
    (db_connection) lib/db_connection.ex:1275: DBConnection.transaction_run/4
    (db_connection) lib/db_connection.ex:1199: DBConnection.run_begin/3
    (db_connection) lib/db_connection.ex:790: DBConnection.transaction/3

Can anyone tell me where I’m going wrong? Happy to provide more info. I’m new to Phoenix / Elixir so I imagine I’m missing something obvious.

For {{2010, 4, 17}, {0, 0, 0, 0}} you should probable use :naive_datetime type. Or you need to set timezone information in your DateTimes.

UPDATE: Oh, sorry. I’ve mistaken {{2010, 4, 17}, {0, 0, 0, 0}} for {{2010, 4, 17}, {0, 0, 0}}, which is what you get from NaiveDateTime.to_erl(naive_datetime), that’s why I suggested the :naive_datetime type.