やりたいこと
下記のような日付と時間を扱った処理をスマートにやるにはどうすればよいか調べてみた。
- 現在のロケールの現在時刻から12時間進めた日時が欲しい
- DBに保存した日付データをn日加算したい
Elixirでの日付/時間
日時は基本的にタプルで持つ。現在時刻はErlangのCalendarモジュールから取得できる。
# UTC現在時刻
utc_now = :calendar.universal_time
{{2015, 10, 20}, {8, 12, 25}}
# ローカル現在時刻
jst_now = :calendar.local_time
{{2015, 10, 20}, {17, 11, 17}}
一般的な日時処理(時間の加算など)
Timexを使うのが一般的か。
use Timex
# 10月31日の1ヶ月後
next_month = {{2015, 10, 31}, {0, 0, 0}} |> Date.from |> Date.shift(months: 1) |> DateConvert.to_erlang_datetime
# 1時間後
an_hour_later = Date.local |> Date.shift(hours: 1) |> DateConvert.to_erlang_datetime
# 2日前
two_days_ago = Date.local |> Date.shift(days: -2) |> DateConvert.to_erlang_datetime
Ecto.DateTimeでの日時処理
Using with Ectoにある方法でModelの日時関係プロパティをTimexモジュールのものにしない場合は下記のようにキャストしてから加算/減算する。
defmodule Demo.Person do
use Demo.Web, :model
schema "people" do
field :name, :string
field :born_at, Ecto.DateTime
timestamps
end
end
john = Repo.get!(Demo.Person, 1)
# Johnの誕生日を7日前にずらす
seven_days_ago = john.born_at
|> Ecto.DateTime.to_erl
|> Date.from
|> Date.shift(days: -7)
|> DateConvert.to_erlang_datetime
|> Ecto.DateTime.from_erl
Repo.update! %{john | born_at: seven_days_ago}
何故こんな面倒なことをするのか?上記ガイドに従って、Timex.Ecto.DateTimeにしても良いんじゃないか?
→日時処理を簡便化するためだけにModelを書き換え、その他モジュールで日時を扱う箇所を全部直し…って大げさすぎませんかね?(感想)
あとがき
Timexを使えば直感的に日時をコントロールできる。素敵。
でもEctoが絡むとどうもスマートになりきれていな気がする。