LoginSignup
9
9

More than 5 years have passed since last update.

[小ネタ] 13日の金曜日

Last updated at Posted at 2015-09-19

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

13日の金曜日~ジェイソンは生きていた

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

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

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


おさらい

Elixirで時間・時刻を扱うとなると最もよく使われるのはTimexでしょう。今回もこれを使います。

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

$ 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を使う設定を追加します。

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

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

$ mix deps.get

プログラム

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

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やらでリストに対して計算する

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

実行結果

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

$ iex -S mix
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では出し方がわからなくなりました。誰か知ってたら教えてください。

$ iex -S mix
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のおかげでちょっと賢くなりました(笑)。

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9