[小ネタ] 13日の金曜日

このところ作業自動化のため「金曜日を見つける」「JSONデータを加工する」機能が必要なツールの作成をやってたんですが、それでふと思いつきました。

「**13日の金曜日～ジェイソンは生きていた**」

ということで**Elixir**でこれから来る13日の金曜日を探してみようというプログラムを書いてみました。

つまり、すんません、完全にネタです。

(2017/7/1追記)
しばらく放置していたらTimexの仕様がかなり変わっていて動作しなくなっていたため
ソースを修正しました。

-----

### **おさらい**

Elixirで時間・時刻を扱うとなると最もよく使われるのは[**Timex**](https://hex.pm/packages/timex)でしょう。今回もこれを使います。

まず`mix new`で作業ディレクトリを作成します。`--sup`はいらなかったかも。
`--module`はモジュール名にCamelCaseを使いたいので。指定しなくてもFridaythe13thというモジュール名になるだけなので特に問題はないです。

```bash
$ mix new fridaythe13th --sup --module FridayThe13th
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/fridaythe13th.ex
* creating test
* creating test/test_helper.exs
* creating test/fridaythe13th_test.exs

Your mix project was created successfully.
You can use mix to compile it, test it, and more:

    cd fridaythe13th
    mix test

Run `mix help` for more commands.

$ cd fridaythe13th
```

次にmix.exsにtimexを使う設定を追加します。

```ex
defmodule FridayThe13th.Mixfile do
  use Mix.Project

  def project do
    [app: :fridaythe13th,
     version: "0.1.0",
     elixir: "~> 1.4",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  # Configuration for the OTP application
  #
  # Type "mix help compile.app" for more information
  def application do
    # Specify extra applications you'll use from Erlang/Elixir
    [extra_applications: [:logger, :timex],
     mod: {FridayThe13th.Application, []}]
  end

  # Dependencies can be Hex packages:
  #
  #   {:my_dep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:my_dep, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
  #
  # Type "mix help deps" for more examples and options
  defp deps do
    [
       {:certifi, "~> 1.0"},
       {:httpoison, "~> 0.11.1"},
       {:timex, "~> 3.0"}
    ]
  end
end
```

あとは忘れずに依存関係ファイルを落としてきておいてください。

```bash
$ mix deps.get
```

### **プログラム**

今回のは短いので全ソース貼り付けます。

```ex
defmodule FridayThe13th do
  @moduledoc """
  Documentation for FridayThe13th.
  Search Fridays the 13th from today within the specified years.
  """

  @doc """
    Friday the 13th.

  ## Examples FridayThe13th.main
    FridayThe13th.main # Without arguments, it returns the fridays the 13th within 3 yeas.

    [ok: "2017/10/13", ok: "2018/04/13", ok: "2018/07/13", ok: "2019/09/13",
     ok: "2019/12/13", ok: "2020/03/13"]

    FridayThe13th.main 5 # Argument is the years for searching.

    [ok: "2017/10/13", ok: "2018/04/13", ok: "2018/07/13", ok: "2019/09/13",
     ok: "2019/12/13", ok: "2020/03/13", ok: "2020/11/13", ok: "2021/08/13",
     ok: "2022/05/13"]


  """
  def thirteens(start_date, years) do
    # 指定日(当日)から初めて最初の13日の金曜日が見つかるまで繰り返す(再帰)
    if Timex.weekday(start_date) != 5 || start_date.day != 13 do
      thirteens(Timex.add(start_date, Timex.Duration.from_days(1)), years)
    else
      # 金曜日ならば以降は7日飛ばしで探していけばよい
      0..years*52 |> Enum.map(fn x ->
        Timex.add(start_date, Timex.Duration.from_days(7*x))
      end) |> Enum.filter(fn x ->
        x.day == 13 # 13日の金曜日だけフィルタする
      end) |> Enum.map(fn x ->
                # IO.inspect(x)
        Timex.Format.DateTime.Formatters.Strftime.format(x, "%Y/%m/%d")
      end)
    end
  end

  def main(args) do # 引数がある場合は今日からその年数分先まで探す
    thirteens(Timex.now, args)
  end

  def main do # 引数がない場合は今日から3年先まで探す
    thirteens(Timex.now, 3)
  end
end

```

**Elixir**らしいかな？というと13日の金曜日を探すところでパイプ演算子(>|)を使っているところぐらいでしょうか。

* とにかくなんでもリストにする
* mapやらfilterやらでリストに対して計算する

あたりは定番の関数型ぽいやりかたとも言えるかもしれません。

### **実行結果**

実行するとこんな感じです。

```bash
$ iex -S mix
```

```iex
Erlang/OTP 18 [erts-7.0] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.0.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> FridayThe13th.main
["2015/11/13", "2016/05/13", "2017/01/13", "2017/10/13", "2018/04/13",
 "2018/07/13"]
iex(2)> FridayThe13th.main 20
["2015/11/13", "2016/05/13", "2017/01/13", "2017/10/13", "2018/04/13",
 "2018/07/13", "2019/09/13", "2019/12/13", "2020/03/13", "2020/11/13",
 "2021/08/13", "2022/05/13", "2023/01/13", "2023/10/13", "2024/09/13",
 "2024/12/13", "2025/06/13", "2026/02/13", "2026/03/13", "2026/11/13",
 "2027/08/13", "2028/10/13", "2029/04/13", "2029/07/13", "2030/09/13",
 "2030/12/13", "2031/06/13", "2032/02/13", "2032/08/13", "2033/05/13",
 "2034/01/13", "2034/10/13", "2035/04/13", "2035/07/13"]
iex(3)>
```

13日の金曜日って思ってたよりも少ないんですね。だいたい2年で3回ちょっとぐらい。直近は2015年11月13日。

----

### **おまけ**

~~Timexをいじっててちょっとおもしろいことに気づきました。~~
残念ながら以下はTimex 3.0では出し方がわからなくなりました。誰か知ってたら教えてください。

```bash
$ iex -S mix
```

```iex
Erlang/OTP 18 [erts-7.0] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.0.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Timex.Date.now
%Timex.DateTime{calendar: :gregorian, day: 19, hour: 12, minute: 43, month: 9,
 ms: 132, second: 24,
 timezone: %Timex.TimezoneInfo{abbreviation: "UTC", from: :min,
  full_name: "UTC", offset_std: 0, offset_utc: 0, until: :max}, year: 2015}
iex(2)> Timex.Date.now("Asia/Tokyo")
%Timex.DateTime{calendar: :gregorian, day: 19, hour: 12, minute: 44, month: 9,
 ms: 387, second: 8,
 timezone: %Timex.TimezoneInfo{abbreviation: "JST",
  from: {:saturday, {{1951, 9, 8}, {1, 0, 0}}}, full_name: "Asia/Tokyo",
  offset_std: 0, offset_utc: 540, until: :max}, year: 2015}
iex(3)>
```

Time Zoneを日本(Asia/Tokyo)に指定すると`from:`に出てくる**1951年9月8日(午前1時0分0秒)**。
**サンフランシスコ平和条約(Treaty of Peace with Japan)**の締結日です。
更に調べると「第二次大戦終了後、日本をアメリカが統治していた間は**サマータイム制**があったがサンフランシスコ平和条約締結後廃止された」と。こういう歴史が今もTime Zoneの情報の中に残ってるんですねえ…何でも見せてくれる**iex**のおかげでちょっと賢くなりました(笑)。