上記Elixirコミュニティ運営/所属の piacere です、ご覧いただいてありがとございます
elixir.jp Slackのスレッドで、Timexをescriptで使おうとしたところ、フォルダを掘らないと動かない … という問題が提起されて、以前、私もこの問題(TimexのdepsであるtzdataのETSテーブルロード問題)に引っかかり、対処方法のコラムを残していましたが、現在のElixir/OTPの構成だと動かなくなっていました
そこで、今まで数年単位でサボり続けてた、Timexと日時系Elixir標準モジュールの比較を改めて行おうと思います
そこそこボリュームありそうなので、何回かに分けて整理します
内容が、面白かったり、役に立ったら、「いいね」よろしくお願いします


ひさびさに主催イベント以外で登壇します 

来週8/5(木)19時から、ハイスキルエンジニア転職サービス「Findy」の下記イベントで、RubyとElixirについてパネルディスカッションします
私がElixirを始めたきっかけと、Rubyに感じ続けた課題、それと最近、Elixir案件でやたら求人募集してる背景などについて、どこまで話せるかは分かりませんが、可能な限り、情報共有したいと思います
https://findy.connpass.com/event/218661
本コラムの検証環境
本コラムは、以下環境で検証しています(Windows WSL2で実施していますが、LinuxやMacでも動作する想定です)
- Windows 10
- WSL2 + Ubuntu 18.04
- OTP 24.0
- Elixir 1.12.0
環境構築が未だの方は、下記コラムをご参考ください
なおZoneinfoが、Windows WSL2だと動きますが、Windowsネイティブだと動かない(≒OSが管理するタイムゾーンデータベースを利用する関係で)ため、Windowsの方は、上記環境構築に記載しているWSL2+Ubuntu 18.04でお試ししてください
Elixir PJは、下記にて構築/REPL起動します
mix new basic
cd basic
defmodule Basic.MixProject do
use Mix.Project
…
defp deps do
[
{:timex, "~> 3.7"},
{:zoneinfo, "~> 0.1"},
…
mix deps.get
iex -S mix
①両方のnowを叩く
まず、現在日時の取得からいきましょう
iex> Timex.now
~U[2021-07-30 02:14:20.217490Z]
iex> DateTime.now!("Etc/UTC")
~U[2021-07-30 02:16:03.809943Z]
標準モジュールは、タイムゾーン指定が必須なので、ちょっちめんどいですね
②日本日時でのnowを叩く
次に、日本日時でのnowです
まずは、各自のタイムゾーンDBの確認です
iex> Tzdata.zone_list
["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers",
"Africa/Asmara", "Africa/Asmera", "Africa/Bamako", "Africa/Bangui",
"Africa/Banjul", "Africa/Bissau", "Africa/Blantyre", "Africa/Brazzaville",
"Africa/Bujumbura", "Africa/Cairo", "Africa/Casablanca", "Africa/Ceuta",
"Africa/Conakry", "Africa/Dakar", "Africa/Dar_es_Salaam", "Africa/Djibouti",
"Africa/Douala", "Africa/El_Aaiun", "Africa/Freetown", "Africa/Gaborone",
"Africa/Harare", "Africa/Johannesburg", "Africa/Juba", "Africa/Kampala",
"Africa/Khartoum", "Africa/Kigali", "Africa/Kinshasa", "Africa/Lagos",
"Africa/Libreville", "Africa/Lome", "Africa/Luanda", "Africa/Lubumbashi",
"Africa/Lusaka", "Africa/Malabo", "Africa/Maputo", "Africa/Maseru",
"Africa/Mbabane", "Africa/Mogadishu", "Africa/Monrovia", "Africa/Nairobi",
"Africa/Ndjamena", "Africa/Niamey", "Africa/Nouakchott", "Africa/Ouagadougou",
"Africa/Porto-Novo", "Africa/Sao_Tome", ...]
iex> Tzdata.zone_list |> Enum.filter(& String.contains?(&1, "Japan"))
["Japan"]
iex> Tzdata.zone_list |> Enum.filter(& String.contains?(&1, "Asia/Tokyo"))
["Asia/Tokyo"]
iex> Zoneinfo.time_zones
["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers",
"Africa/Bangui", "Africa/Bissau", "Africa/Blantyre", "Africa/Casablanca",
"Africa/Ceuta", "Africa/El_Aaiun", "Africa/Johannesburg", "Africa/Juba",
"Africa/Khartoum", "Africa/Monrovia", "Africa/Ndjamena", "Africa/Sao_Tome",
"Africa/Tunis", "Africa/Windhoek", "America/Adak", "America/Anchorage",
"America/Anguilla", "America/Araguaina", "America/Argentina/La_Rioja",
"America/Argentina/Rio_Gallegos", "America/Argentina/Salta",
"America/Argentina/San_Juan", "America/Argentina/San_Luis",
"America/Argentina/Tucuman", "America/Argentina/Ushuaia", "America/Aruba",
"America/Asuncion", "America/Atikokan", "America/Bahia",
"America/Bahia_Banderas", "America/Barbados", "America/Belem",
"America/Belize", "America/Blanc-Sablon", "America/Boa_Vista",
"America/Bogota", "America/Boise", "America/Buenos_Aires",
"America/Cambridge_Bay", "America/Campo_Grande", "America/Cancun",
"America/Caracas", "America/Catamarca", "America/Cayenne", "America/Cayman",
"America/Chicago", ...]
iex> Zoneinfo.time_zones |> Enum.filter(& String.contains?(&1, "Japan"))
["Japan", "right/Japan"]
iex> Zoneinfo.time_zones |> Enum.filter(& String.contains?(&1, "Asia/Tokyo"))
どうやらZoneinfoの方は、「Asia/Tokyo」が存在しないようなので、「Japan」で検証していきます
さて、タイムゾーンも確認できたので、日本日時を取得してみます
iex> Timex.now("Japan")
# DateTime<2021-07-30 19:50:58.878171+09:00 JST Japan>
iex> DateTime.now!("Japan")
# DateTime<2021-07-30 19:50:17.137880+09:00 JST Japan>
タイムゾーン設定のタネ明かし
Timexをインストールしている状況下だと、下記の通り、Elixir標準モジュールもTimex(つまりtzdata)をタイムゾーンDBとして利用するようです
iex> Calendar.get_time_zone_database
Timex.Timezone.Database
ちなみに、Timex未インストールだと、下記のようになっており、UTCしか対応していません(コレがElixir標準モジュールのデフォルト)
iex> Calendar.get_time_zone_database
Calendar.UTCOnlyTimeZoneDatabase
ちなみに、DateTime.now!()の第2引数で指定している、タイムゾーンDBの指定は、configでデフォルト指定することも可能です
なお、下記ファイルは、デフォルトではフォルダ毎、存在しないため、フォルダ作成とファイル作成を行ってください
import Config
config :elixir, time_zone_database: Zoneinfo.TimeZoneDatabase
設定後、iexを再起動すると、下記のようになります
iex> Calendar.get_time_zone_database
Zoneinfo.TimeZoneDatabase
iex> Zoneinfo.time_zones |> Enum.filter(& String.contains?(&1, "Japan"))
["Japan", "right/Japan"]
iex> timex = Timex.now("Japan"); datetime = DateTime.now!("Japan")
# DateTime<2021-07-30 20:54:13.158207+09:00 JST Japan>
iex> timex
# DateTime<2021-07-30 20:54:13.124480+09:00 JST Japan>
iex> datetime
# DateTime<2021-07-30 20:54:13.158207+09:00 JST Japan>
最後に
ひとまず、タイムゾーン変更がTimexでもできることが分かり、解決の光がちょっと見えてきた気がします
次回は、上記タイムゾーン変更した後のTimexをescriptで動かし、エラー解消できるかトライします