Ecto 默认使用的是UTC时间, 它要比中国区的本地时间晚 8 小时.
2016-07-21 更新: 模块已经添加到 , 添加
{:ecto_timestamps, "~> 1.0"}
依赖到mix.exs
即可, 详细信息参考项目注意,
:autogenerate
需要 Ecto2.0
的支持. 所以要使用这个自定义选项, 需要升级到 Ecto 2.0
说明了这个问题.
经过查找资料, 我们在 中找到了 timestamps
宏的选项 :autogenerate
, 它的格式为一个三元组, 分别是模块, 函数, 参数
{Module, :function, []}
Ecto 的 timestamps
的时间戳是自动生成的, 同时Ecto 也给我们提供了自定义的方法, 我们参考上面的Ecto源码实现本地时间的插入
首先编写一个新的Localtime
模块, 把UTC时间修改为本地时间, 定义如下, 只是替换了两个函数, 分别是:
:erlang.localtime
替换:erlang.universaltime
:calendar.now_to_local_time
替换:calendar.now_to_datetime
defmodule Test.Localtime do def autogenerate(precision \\ :sec) def autogenerate(:sec) do {date, {h, m, s}} = :erlang.localtime erl_load({date, {h, m, s, 0}}) end def autogenerate(:usec) do timestamp = {_, _, usec} = :os.timestamp {date, {h, m, s}} = :calendar.now_to_local_time(timestamp) erl_load({date, {h, m, s, usec}}) end def erl_load({ {year, month, day}, {hour, min, sec, usec}}) do %Ecto.DateTime{ year: year, month: month, day: day, hour: hour, min: min, sec: sec, usec: usec } endend
在模型的模块属性中声明 @timestamps_opts
时间戳选项:
@timestamps_opts [ autogenerate: {Test.Localtime, :autogenerate, [:sec]}]
然后我们用一个例子来证实
模型的公共模块
require Loggerdefmodule Test.Model do defmacro __using__(_opts) do quote do import Ecto.Query use Ecto.Schema alias Ecto.Changeset alias Test.Repo # 这里为了演示时差的问题, 先注释掉 # @timestamps_opts [autogenerate: {Test.Localtime, :autogenerate, [:sec]}] end endend
创建一个角色
模型
defmodule Test.Model.Role do @moduledoc """ 角色表 """ use Test.Model schema "role" do field :name, :string # 角色名称 timestamps endend
创建移植脚本
➜ mix ecto.gen.migration create_role_table -r Test.Repo* creating priv/repo/migrations* creating priv/repo/migrations/20160713121457_create_role_table.exs
内容如下
defmodule Test.Repo.Migrations.CreateRoleTable do use Ecto.Migration def up do create table(:role) do add :name, :string # 角色名称 timestamps end end def down do drop table(:role) endend
创建表
➜ ✗ mix ecto.migrateCompiling 14 files (.ex)20:47:24.510 [info] == Running Test.Repo.Migrations.CreateRoleTable.up/0 forward20:47:24.511 [info] create table role20:47:24.534 [info] == Migrated in 0.0s
角色模型修改为如下, 增加了测试函数:
defmodule Test.Model.Role do @moduledoc """ 角色表 """ use Test.Model schema "role" do field :name, :string # 角色名称 timestamps end def insert(map) do Map.merge(%__MODULE__{}, map) |> Repo.insert end def test_insert do row = %{ name: "技术总监" } insert(row) endend
启动IEx测试, 现在的时间是2016-07-13 21:02:49, 插入的时间为2016-07-13 13:02:49, 晚了8个小时
(输出手工格式化, 以便阅读)
iex> Test.Model.Role.test_insertQUERY OK db=6.9ms decode=1.0ms queue=0.9msINSERT INTO "role" ("name","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["技术总监", { {2016, 7, 13}, {13, 2, 49, 0}}, { {2016, 7, 13}, {13, 2, 49, 0}}]{:ok, %Test.Model.Role{ __meta__: #Ecto.Schema.Metadata<:loaded, "role">, id: 6, inserted_at: #Ecto.DateTime<2016-07-13 13:02:49>, name: "技术总监", updated_at: #Ecto.DateTime<2016-07-13 13:02:49>}}
数据库中插入的数据为
select * from role;+------+----------+---------------------+---------------------+| id | name | inserted_at | updated_at ||------+----------+---------------------+---------------------|| 6 | 技术总监 | 2016-07-13 13:02:49 | 2016-07-13 13:02:49 |+------+----------+---------------------+---------------------+
取消上面 Test.Model
模块的属性声明的注释, 并重新编译, 进入IEx
require Loggerdefmodule Test.Model do defmacro __using__(_opts) do quote do import Ecto.Query use Ecto.Schema alias Ecto.Changeset alias Test.Repo # 这里为了演示时差的问题, 先注释掉 @timestamps_opts [autogenerate: {Test.Localtime, :autogenerate, [:sec]}] end endend
再次执行
iex(18)> Test.Model.Role.test_insert QUERY OK db=8.5ms decode=1.0ms queue=1.1msINSERT INTO "role" ("name","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["技术总监", { {2016, 7, 13}, {21, 4, 58, 0}}, { {2016, 7, 13}, {21, 4, 58, 0}}]{:ok, %Test.Model.Role{ __meta__: #Ecto.Schema.Metadata<:loaded, "role">, id: 7, inserted_at: #Ecto.DateTime<2016-07-13 21:04:58>, name: "技术总监", updated_at: #Ecto.DateTime<2016-07-13 21:04:58>}}
数据为
select * from role;+------+----------+---------------------+---------------------+| id | name | inserted_at | updated_at ||------+----------+---------------------+---------------------|| 6 | 技术总监 | 2016-07-13 13:02:49 | 2016-07-13 13:02:49 || 7 | 技术总监 | 2016-07-13 21:04:58 | 2016-07-13 21:04:58 |+------+----------+---------------------+---------------------+