Engineering

14 August, 2017

Timex and Ecto: Troubleshooting

A while ago I needed to use a better notion of time in our project, and after a little bit of research I found Timex and Timex Ecto library for Elixir.

David Magalhães

Software Engineer

Timex and Timex Ecto Troubleshooting - Coletiv Blog

It has been 5 months since I started learning Elixir and Phoenix, and has been quite a ride. Most of the experience has been great but sometimes I find some issues that are hard to tackle.

A while ago I needed to use a better notion of time in our project, and after a little bit of research I found Timex and Timex Ecto library for Elixir.

Timex helps with all the necessary handling of time operations like conversions to a time unit, calculations for the difference between two date times, usage of timezones, etc. You can have a look at all the functionalities in the HexDocs. Timex Ecto applies that functionality to Ecto to be used with a database.

After using this library for some time I needed to make a query to PostgreSQL to delete all the rows before a certain date time. A normal query would be like the following:

MyApp.Model |> where([m], m.expire_date < ^Timezone.convert(Timex.now, “Europe/Copenhagen”)) |> Repo.delete_all

But the following error happened,

[error] #PID<0.514.0> running MyApp.Web.Endpoint terminated Server: localhost:4000 (http) Request: POST /api/auth/reset ** (exit) an exception was raised: ** (FunctionClauseError) no function clause matching in MyApp.PostgresTypes.encode_tuple/5 (my_app) lib/postgrex/type_module.ex:717: MyApp.PostgresTypes.encode_tuple({{{2017, 8, 9}, {22, 44, 59, 175265}}, “Europe/Copenhagen”}, 1, nil, {MyApp.PostgresTypes, 1638460}, []) (my_app) deps/postgrex/lib/postgrex/type_module.ex:717: MyApp.PostgresTypes.Postgrex.Extensions.Record/3 (my_app) deps/postgrex/lib/postgrex/type_module.ex:717: MyApp.PostgresTypes.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:1199: DBConnection.run_begin/3 (db_connection) lib/db_connection.ex:584: DBConnection.prepare_execute/4 (ecto) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.prepare_execute/5 (ecto) lib/ecto/adapters/sql.ex:243: Ecto.Adapters.SQL.sql_call/6 (ecto) lib/ecto/adapters/sql.ex:431: Ecto.Adapters.SQL.execute_and_cache/7 (my_app) lib/my_app/auth/auth_controller.ex:162: MyApp.Web.AuthController.reset/2 (my_app) lib/my_app/auth/auth_controller.ex:1: MyApp.Web.AuthController.action/2 (my_app) lib/my_app/auth/auth_controller.ex:1: MyApp.Web.AuthController.phoenix_controller_pipeline/2 (my_app) lib/my_app/web/endpoint.ex:1: MyApp.Web.Endpoint.instrument/4 (phoenix) lib/phoenix/router.ex:277: Phoenix.Router.__call__/1 (my_app) lib/my_app/web/endpoint.ex:1: MyApp.Web.Endpoint.plug_builder_call/2 (my_app) lib/plug/debugger.ex:123: MyApp.Web.Endpoint.”call (overridable 3)”/2 (my_app) lib/my_app/web/endpoint.ex:1: MyApp.Web.Endpoint.call/2 (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4

After a bit of debugging I found a solution for this problem, but it was not a clean one. Because I’m using DateTimeWithTimezone I needed to create a new custom type in the database indicated here.

CREATE TYPE datetimetz AS ( dt timestamptz, tz varchar );

This structure represent the normal timezone type available in PostgreSQL (timestamptz) and the location of the timezone in format string. To query this structure my solution was to use the fragment function and check for the dt parameter. For example:

MyApp.Table |> where([ur], fragment(“(expire_date).dt < ?”, ^Timex.now)) |> Repo.delete_all

After talking with the @gotbones (creator of Timex), he helped me with the solution:

So the solution to your issues is this: When using query fragments on custom types, you have to specify the type of the object (because Ecto can’t know what the type is), you do this like so: where: u.datetime_field > type(^Timezone.convert(….), Timex.Ecto.DateTimeWithTimezone)

The final solution looks like this:

MyApp.Model |> where([ur], ur.expire_date < type(^Timezone.convert(Timex.now, "Europe/Lisbon"), Timex.Ecto.DateTimeWithTimezone)) |> Repo.delete_all

Finally, an happy ending for this issue and a better understanding how Ecto and Timex works!

Elixir

Software Development

Ecto

Timex

Join our newsletter

Be part of our community and stay up to date with the latest blog posts.

Subscribe

Join our newsletter

Be part of our community and stay up to date with the latest blog posts.

Subscribe

You might also like...

Go back to blogNext
How to support a list of uploads as input with Absinthe GraphQL

Engineering

26 July, 2022

How to support a list of uploads as input with Absinthe GraphQL

As you might guess, in our day-to-day, we write GraphQL queries and mutations for Phoenix applications using Absinthe to be able to create, read, update and delete records.

Nuno Marinho

Software Engineer

Flutter Navigator 2.0 Made Easy with Auto Router - Coletiv Blog

Engineering

04 January, 2022

Flutter Navigator 2.0 Made Easy with Auto Router

If you are a Flutter developer you might have heard about or even tried the “new” way of navigating with Navigator 2.0, which might be one of the most controversial APIs I have seen.

António Valente

Software Engineer

Enabling PostgreSQL cron jobs on AWS RDS - Coletiv Blog

Engineering

04 November, 2021

Enabling PostgreSQL cron jobs on AWS RDS

A database cron job is a process for scheduling a procedure or command on your database to automate repetitive tasks. By default, cron jobs are disabled on PostgreSQL instances. Here is how you can enable them on Amazon Web Services (AWS) RDS console.

Nuno Marinho

Software Engineer

Go back to blogNext