bitwalker / timex_ecto

An adapter for using Timex DateTimes with Ecto
MIT License
162 stars 68 forks source link

timestamptz: entity updates despite identical time #79

Open eteubert opened 6 years ago

eteubert commented 6 years ago

I expect there to be no database UPDATE when I add a date to the changeset that is identical to the one in the database, just like with native dates. However it looks like there is always an update happening. Is this a bug or am I using it wrong?

iex|34 ▶ {:ok, some_date} = "2018-05-15T13:05:00.000+0000"|> Timex.parse!("{ISO:Extended}")|> Timex.Timezone.convert("Europe/Berlin") |> Timex.Ecto.TimestampWithTimezone.cast()
{:ok, #DateTime<2018-05-15 15:05:00.000+02:00 CEST Europe/Berlin>}
iex|35 ▶ episode = Directory.get_episode! 1
SELECT ... [1]
%Directory.Episode{
  ...
  publication_date: #DateTime<2018-05-15 15:05:00+02:00 CEST Europe/Berlin>,
  updated_at: #DateTime<2018-05-15 15:20:50Z>
}
iex|36 ▶ Directory.update_episode(episode, %{publication_date: some_date})
begin []
UPDATE "episodes" SET "publication_date" = $1, "updated_at" = $2 WHERE "id" = $3 [{{2018, 5, 15}, {13, 5, 0, 0}}, {{2018, 5, 15}, {15, 22, 4, 0}}, 1]
SELECT ...
{:ok,
 %Directory.Episode{
   publication_date: #DateTime<2018-05-15 15:05:00.000+02:00 CEST Europe/Berlin>,
   updated_at: #DateTime<2018-05-15 15:22:04Z>
 }}

Context (shortened):

  def update_episode(%Episode{} = episode, attrs) do
    result =
      episode
      |> Episode.changeset(attrs)
      |> Repo.update()
  end
defmodule Directory.Episode do
  use Ecto.Schema

  import Ecto.Changeset
  import Ecto.Query

  @timestamps_opts [
    type: Timex.Ecto.DateTime,
    autogenerate: {Timex.Ecto.DateTime, :autogenerate, []}
  ]

  schema "episodes" do
    field(:publication_date, Timex.Ecto.TimestampWithTimezone)

    timestamps()
  end

  @doc false
  def changeset(%Episode{} = episode, attrs) do
    episode
    |> cast(attrs, [
      :publication_date,
    ])
  end
end
eteubert commented 6 years ago

I digged deep enough to find a workaround but not far enough to say with certainty where the bug lies.

It's a precision issue.

"2018-05-25T09:00:00+0000"
vs
"2018-05-25T09:00:00.000+0000"

In my understanding they should be treated equally when I pass them into Timex.parse!(datetime, "{ISO:Extended}") regarding changesets but they are not.

My workaround is to remove the ms part from the source before parsing:

datetime
|> String.replace(".000+", "+")
|> Timex.parse!("{ISO:Extended}")