kuroda
How to upgrade an app using timex_ecto to Phoenix 1.4 and Ecto 3.0
I have a application built on Phoenix 1.3. I use timex and timex_ecto in order to deal with datetimes with time zone.
defp deps do
[
{:phoenix, "~> 1.3.3"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:timex, "~> 3.1"},
{:timex_ecto, "~> 3.2"}
]
end
The schema module MyApp.Schedule.Item is defined as follows:
defmodule MyApp.Schedule.Item do
use Ecto.Schema
use Timex.Ecto.Timestamps
schema "items" do
field(:name, :string)
field(:starts_at, Timex.Ecto.DateTime)
end
end
In the priv/repo/seeds.exs, I inserts a record like this:
import MyApp.Repo
time0 = Timex.now("Asia/Tokyo") |> Timex.beginning_of_day()
insert!(%MyApp.Schedule.Item{
name: "Example",
starts_at: Timex.shift(time0, days: 1, hours: 10)
})
I need some adivice to upgrade my application to Phoenix 1.4 (rc.3) and Ecto 3.0.
Firstly, I removed timex_ecto from mix.exs because timex_ecto does not work with Ecto 3.0.
defp deps do
[
{:phoenix, "~> 1.4.0-rc"},
{:phoenix_pubsub, "~> 1.1"},
{:phoenix_ecto, "~> 4.0"},
{:ecto_sql, "~> 3.0-rc"},
{:postgrex, ">= 0.0.0-rc"},
{:phoenix_html, "~> 2.11"},
{:phoenix_live_reload, "~> 1.2-rc", only: :dev},
{:gettext, "~> 0.11"},
{:jason, "~> 1.0"},
{:plug_cowboy, "~> 2.0"},
{:timex, "~> 3.1"}
]
end
Secondry, I rewrote the MyApp.Schedule.Item module as follows:
defmodule MyApp.Schedule.Item do
use Ecto.Schema
schema "items" do
field(:name, :string)
field(:starts_at, :utc_datetime)
end
end
Then, the priv/repo/seeds.exs came to cause this error:
** (ArgumentError) :utc_datetime expects the time zone to be "Etc/UTC", got `#DateTime<2018-11-03 11:00:00+09:00 JST Asia/Tokyo>`
Of course, to fix this error I can rewrite the script like this:
time0 =
Timex.now("Asia/Tokyo")
|> Timex.beginning_of_day()
|> Timex.Timezone.convert("Etc/UTC")
insert!(%MyApp.Schedule.Item{
name: "Example",
starts_at: Timex.shift(time0, days: 1, hours: 10)
})
But, I think such a change is a little burdensome, because my app has quite a lot similar expressions.
So, taking advice in Compile error with ecto 3.0 and timex_ecto 3.3 · Issue #2770 · elixir-ecto/ecto · GitHub, I created a custom Ecto type for my application:
defmodule MyApp.Ecto.DatetimeWithTimezone do
@behaviour Ecto.Type
def type, do: :datetime
def cast(%DateTime{} = dt), do: {:ok, dt}
def cast(_), do: :error
def load(%NaiveDateTime{} = ndt), do: DateTime.from_naive(ndt, "Etc/UTC")
def load(_), do: :error
def dump(%DateTime{} = datetime) do
case Timex.Timezone.convert(datetime, "Etc/UTC") do
%DateTime{} = dt -> {:ok, dt}
{:error, _} -> :error
end
end
def dump(_), do: :error
end
Then, I made following modifications on my Model.User module:
defmodule MyApp.Schedule.Item do
use Ecto.Schema
# use Timex.Ecto.Timestamps # <- Removed
alias MyApp.Ecto.DatetimeWithTimezone # <- Added
schema "users" do
field(:name, :string, null: false)
# field(:starts_at, Timex.Ecto.Datetime) # <- Removed
field(:starts_at, DatetimeWithTimezone) # <- Added
...
With these changes, my app works just like before.
Am I upgrading my app in a correct way? Or should I basically change its design?
Most Liked
michalmuskala
Just to clarify - Ecto uses the native elixir datetime types since Ecto 2.1 released almost 2 years ago. If the timex_ecto package is not needed now, it wasn’t needed back then as well. It’s just that now the old custom ecto datetime types were removed after being deprecated for almost 2 years.
The things in this area that Ecto 3 changes are that the low-level “raw” query interface also returns the elixir types instead of the erlang-style tuples and that we have the explicit separation of _usec and non-usec types.
bitwalker
@kurtome The approach above is fine, but probably shouldn’t be called DateTimeWithTimezone, since it doesn’t actually store the timezone, it just converts to and from UTC, but naming aside, yeah that’s close enough. You can always refer to the current Timex.Ecto implementations for reference too.
I’d be glad to merge a PR with a link! I just don’t have time to throw something together and test that it is correct at the moment
- normally I have a bit more free time, but I’m more swamped than I have been in a long time, so my backlog has gotten a bit out of hand haha
I don’t really get what makes Ecto 3.0 that different from previous versions as it relates to these datetime types
Ecto 3.0 makes use of the new calendar types in Elixir, rather than its own Ecto.* calendar types. This is important because it means the last remaining piece needed to have Ecto handle everything natively (well, with a timezone library) is timezone support (which is under construction as far as I’m aware). But if you don’t need to support arbitrary timezones in the database, Ecto 3 already supports NaiveDateTimes and DateTimes in UTC (as well as the other types of course). The bottom line is that Timex becomes a support library for the calendar types, but is no longer an active part of the Elixir/Ecto date/time interaction, since it is all natively supported :). This is ultimately one of the main goals of adding the Elixir calendar types, is to unify these things so that libraries are all mutually compatible with one another, without needing explicit adapters between them (which is effectively what Timex.Ecto was).
dhanson
One note on this subject - I migrated from using Timex to using naive datetimes when I upgraded after reading this thread. My services started erroring with data from my frontend that had been working prior to the upgrade. I use postgres for the database. timestamps were fine. the problem was in fields like ‘appointment datetime’ - my frontend JS datetime pickers were not sending seconds ie “2018-12-12T16:00” was sent - and the cast in my models was returning invalid date format errors. iso 8601 does not mandate seconds from what I read - but NaiveDateTime.from_iso8601 throws the exception, and the Timex usage/implementation did not error. So I rolled my ownNaiveDateTimeNoSeconds Ecto type to deal with this.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









