I’ll chime in, because this is a subject that always annoyed me about how it was handled by default and I need to manually change things to my taste (I’m using PostgreSQL).
First, I want my timestamps to be timestamptz in the DB. Forget that it’s badly named (confusion about timezone), it is the correct type to represent a “point in time”. If your DB uses timestamp (without tz), it’s ambiguous and someone needing to interface with the DB outside your app won’t have the slightest clue what they represent (is it local time (ouch…) or UTC?). Using the correct type makes confusion impossible.
Anyway, most of the things I change have been mentioned in this thread.
Make sure migration timestamps are timestamptz by default and have a default, which makes it easier to insert rows in DB manually for testing. It’s important that the default timezone is set to UTC (it could be set in the DB, but I prefer to have my system work either way. It’s important because Ecto was doing some casting where where: token.inserted_at > ago(^days, "day") was converted to SQL inserted_at > $2::timestamp + (interval ...) and the only this conversion works correctly is if the default timezone is UTC.
config :my_app, MyApp.Repo,
migration_primary_key: [type: :identity],
migration_timestamps: [type: :timestamptz, default: {:fragment, "now()"}],
after_connect: {Postgrex, :query!, ["SET TimeZone TO 'UTC';", []]}
As already mentioned, I have a custom schema:
defmodule MyApp.Schema do
defmacro __using__(_) do
quote do
use Ecto.Schema
@timestamps_opts [type: :utc_datetime_usec]
end
end
end
I don’t use the generators much, but still I have a private copy of the phx.gen.schema templates.
schema.ex, make it use my schema:
defmodule <%= inspect schema.module %> do
use <%= hd(Module.split(schema.repo)) %>.Schema
migration.exs, make sure fields with utc_datetime and utc_datetime_usec are converted correctly to timestamptz, and at the same time make sure text is always used because I can’t stand the sight of varchar(255):
<%
internal_type_mapper = fn
:string -> :text
:utc_datetime_usec -> :timestamptz
other -> other
end
type_mapper = fn
:utc_datetime -> ":timestamptz, size: 0"
{:array, :utc_datetime} -> "{:array, :timestamptz}, size: 0"
{:array, subtype} -> inspect({:array, internal_type_mapper.(subtype)})
other -> inspect(internal_type_mapper.(other))
end
%>defmodule <%= inspect schema.repo %>.Migrations.Create<%= Macro.camelize(schema.table) %> do
...
... <%= type_mapper.(Mix.Phoenix.Schema.type_for_migration(v)) %> ...