Confused with storing dates with Calendar/Ecto

I am using calendar, ecto and phoenix with mysql.

I have the following schema:

schema "foo" do
  field :scheduled_at, Calecto.DateTimeUTC, null: true

  timestamps()
end

def changeset(struct, :schedule, params) do
  struct
  |> cast(params, [:scheduled_at])
end

I would like my users to edit/create dates in “America/Chicago” (for the sake of this example) but they have to be stored as UTC.

The problem I have, is that, obviously, if I don’t process the form data in any way, it will get saved to the DB as UTC (eg, the user selected “12:30” and it will be stored in the DB as “12:30” but will be read later as UTC). Ideally, I would have to convert it first from “America/Chicago” to UTC before it gets saved to the database.

The ugly option is to take the params from the form and do some time arithmetic with them before they are passed to changeset, but this feels quite ugly.

I also thought of converting the casted date to naive and then back to UTC, but I get an error:

def changeset(struct, :schedule, params) do
  struct
  |> cast(params, [:scheduled_at])
  |> update_change(:scheduled_at, fn(date) ->
       {:ok, date_time} = Calendar.DateTime.to_naive(date) |> Calendar.DateTime.from_naive("America/Chicago")
       date_time
     end)
end

I think I am overcomplicating things. Any help is appreciated.

Thanks in adbance

1 Like

Your change seems correct as I am not sure if Calendar.DateTime has an API to convert between timezones without shifting the datetime.

Another option is to send a pull request to Calecto.DateTimeUTC.cast to also receive the timezone. This way you can pass the timezone as a hidden field and Calecto.DateTimeUTC will parse it considering the proper timezone. The conversion to UTC should happen on dumping the value before writing to the database.

Thanks, @josevalim. I will check with Lau and see what he thinks about that PR.