Custom ecto type :naive_datetime insert errors

I have a task schema which has a start_at field which is a custom echo type Type.DateTime

field        :start_at,             App.Type.DateTime

Now im trying to cast some strings to :naive_datetime in a custom Ecto.Type
but all I’m getting is;

value `~N[2017-09-04 09:00:00]` for `App.Task.start_at` in `insert` does not match type App.Type.DateTime

this is my custom Ecto.Type at the moment.

defmodule App.Type.DateTime do
  @behaviour Ecto.Type

  def type, do: :naive_datetime

  def load(value), do: {:ok, value}
  def dump(:naive_datetime = datetime), do: {:ok, datetime}
  def dump(_), do: :error

  def cast(string) when is_binary(string) do
    case cast_date_time(string) do
      {:ok, match} -> {:ok, match}
      _            -> cast_date(string)
    end
  end

  @doc """
    Type.DateTime.cast_date("01/12/2017") 
  """
  def cast_date(string) when is_binary(string) do
    case Regex.run(~r/^([0-9]+)\/([0-9]+)\/([0-9]+)$/, string) do
      [_match, d, mm, y] -> Ecto.Type.cast(:naive_datetime, "#{y}-#{mm}-#{d} 09:00:00")
      nil -> :error
    end
  end

  @doc """
    Type.DateTime.cast_date_time("31/12/2017 12:00:00") 
  """
  def cast_date_time(string) when is_binary(string) do 
    case Regex.run(~r/^([0-9]+)\/([0-9]+)\/([0-9]+) ([0-9]+):([0-9]+):([0-9]+)$/, string) do
      [_match, d, mm, y, h, m, s] -> Ecto.Type.cast(:naive_datetime, "#{y}-#{mm}-#{d} #{h}:#{m}:#{s}")
      nil -> :error
    end
  end

  def cast(:naive_datetime = datetime), do: {:ok, datetime}

end

I think there are a few problems with your code but this seems to be main one. I asssume you want to have a patterna match that would catch values of type “naive_datetime” but it won’t work this way. It would match only if datetime was an atom of :naive_datetime which obviously does not make sense.

Check out this blog post and some example code by @michalmuskala that will help you to implement easier/proper solution (down in the post). There’s a pattern of delegating to Ecto.Type too shown, so you do not loose all the functionality that you have by default.

What is exactly the thing you’re trying to do here, because it’s not clear. Do you have a custom date/time picker that you have to parse?

3 Likes

thanks, I will dive into it.

what im trying to do that sometimes im only getting a date string from the frontend, and I need to translate it to to date time.

Yes, then most likely you want to delegate to Ecto.Type so you keep all the current behavior, and only add the cast function variant that would pattern match on the string you want to capture.

1 Like