Storing date from date_select

How does one go about storing datetime values from a date_select field in the database?

Here is my Ecto model:

defmodule MyApp.User do
  use MyApp.Web, :model

  schema "users" do
    field :birthday, :utc_datetime
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:birthday])
  end
end

Here is my template:

<%= date_select f, :birthday, builder: fn b -> %>
  <%= b.(:day, prompt: "", class: "custom-select") %> /
  <%= b.(:month, prompt: "", class: "custom-select") %> /
  <%= b.(:year, options: Timex.now.year..(Timex.now.year - 100), prompt: "", class: "custom-select") %>
<% end %>

Here is my controller:

def update(conn, %{"user" => user_params}) do
  user = Guardian.Plug.current_resource(conn)
  changeset = User.changeset(user, user_params)
  case Repo.update(changeset) do
    {:ok, _user} ->
      conn
      |> put_flash(:success, gettext("Profile updated!"))
      |> redirect(to: public_profile_path(conn, :show, user))
    {:error, changeset} ->
      conn
      |> render("edit.html", changeset: changeset)
  end
end

I can successfully submit the form, but the birthday is never written to the database

1 Like

What do the logs show when that action is hit?

1 Like

[info] POST /profile
[debug] QUERY OK source=“users” db=1.0ms decode=0.1ms
SELECT … FROM “users” AS u0 WHERE (u0.“id” = $1) [1]
[debug] Processing by Helheim.ProfileController.update/2
Parameters: %{"_csrf_token" => “aCEAUVpdQ0IOZBsZAyJnE0AKBTI9JgAAGp34nir096OsZN4k73TbyA==”, “_method” => “put”, “_utf8” => “✓”, “user” => %{“birthday” => %{“day” => “31”, “month” => “12”, “year” => “2017”}}}
Pipelines: [:browser, :browser_auth]
[info] Sent 200 in 6ms

It appears that there is some sort of implicit validation taking place since f.errors[:birthday] in the template returns “is invalid”

I have also tried using Timex like so:

field :birthday, Timex.Ecto.DateTime

which just gives an error:

no function clause matching in Timex.Protocol.Tuple.to_datetime/2

Trying with a Timex date field instead:

field :birthday, Timex.Ecto.Date

gives another error:

could not encode date/time: {2017, 12, 31}

1 Like

I wonder if you need to use field :birthday, :date.

I wonder if that’s because you’re using :utc_datetime and you aren’t passing in a time.

2 Likes

You are right. Changing the column in the database to :date fixes the problem.
I find it weird though that it shouldn’t be possible to store a date value in a datetime field and just have the time-part assumed as midnight.
In this case it isn’t a big problem, but there might be cases where it is important to have the time-part in the database as well as the date in case you want to perform timezone-sensitive checks on the date…

2 Likes