Timex parsing invalid dates and the db is blowing up

Thoughts on how I can catch this type of error? I have an invalid date ie 11/31/2910. There is no 31st day of November. But my little logic does not catch that and timex seems to parse it all the same.

pry(2)> Timex.parse(start_date_string, "%m/%d/%Y", :strftime)
{:ok, ~N[2910-11-31 00:00:00]}

Heres my changeset validation gate

def parse_date(cs, key, string) do
    case Timex.parse(string, "%m/%d/%Y", :strftime) do
      {:ok, date} ->
        cs |> put_change(key, date)

      {:error, _} ->
        cs |> add_error("#{key}_string" |> String.to_atom(), "not a valid date")
    end
  end

but as I said since timex parses the date it passes the gate on its way to the db where it blows up

[error] #PID<0.567.0> running BeffectWeb.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /departments/early-childhood/safe-and-together/client/1/spi/1
** (exit) an exception was raised:
    ** (ErlangError) Erlang error: :if_clause
        (stdlib) calendar.erl:117: :calendar.date_to_gregorian_days/3
        (ecto) 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:1079: DBConnection.describe_run/5
        (db_connection) lib/db_connection.ex:1150: anonymous fn/4 in DBConnection.run_meter/5
        (db_connection) lib/db_connection.ex:592: DBConnection.prepare_execute/4
        (ecto) lib/ecto/adapters/postgres/connection.ex:86: Ecto.Adapters.Postgres.Connection.execute/4
        (ecto) lib/ecto/adapters/sql.ex:256: Ecto.Adapters.SQL.sql_call/6
        (ecto) lib/ecto/adapters/sql.ex:542: Ecto.Adapters.SQL.struct/8
        (ecto) lib/ecto/repo/schema.ex:547: Ecto.Repo.Schema.apply/4
        (ecto) lib/ecto/repo/schema.ex:292: anonymous fn/14 in Ecto.Repo.Schema.do_update/4
        (ecto) lib/ecto/repo/schema.ex:774: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
        (ecto) lib/ecto/adapters/sql.ex:576: anonymous fn/3 in Ecto.Adapters.SQL.do_transaction/3
        (db_connection) lib/db_connection.ex:1283: DBConnection.transaction_run/4
        (db_connection) lib/db_connection.ex:1207: DBConnection.run_begin/3
        (db_connection) lib/db_connection.ex:798: DBConnection.transaction/3
        (beffect) lib/beffect_web/controllers/spi_controller.ex:156: BeffectWeb.SPIController.update/2
        (beffect) lib/beffect_web/controllers/spi_controller.ex:1: BeffectWeb.SPIController.action/2
        (beffect) lib/beffect_web/controllers/spi_controller.ex:1: BeffectWeb.SPIController.phoenix_controller_pipeline/2
        (beffect) lib/beffect_web/endpoint.ex:1: BeffectWeb.Endpoint.instrument/4

What should I do for this?

1 Like

I think part of the problem is that Timex.parse/3 is only returning a NaiveDateTime when you’ll probably want a DateTime.

This is slightly improved:

iex(6)> Timex.parse!("11/31/2910", "%m/%d/%Y", :strftime) |> Timex.to_datetime("America/Chicago")
** (ErlangError) Erlang error: :if_clause
    (stdlib) calendar.erl:117: :calendar.date_to_gregorian_days/3
    (stdlib) calendar.erl:138: :calendar.datetime_to_gregorian_seconds/1
    (timex) lib/datetime/helpers.ex:32: Timex.DateTime.Helpers.construct/2

Although it seems that Timex should be returning an {:error, term()} instead of raising that Erlang error. Maybe that’s a bug in Timex?

2 Likes

Yeah, this looks like a bug in timex to me. IMHO it should never allow creating invalid dates when parsing.

2 Likes