Created
August 6, 2016 10:06
-
-
Save Jeweller-Tsai/49ec7b14bc0d42a122ac262f6f9b5477 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule DateRange do | |
defstruct first: nil, last: nil, first_gregorian_days: nil, last_greogorian_days: nil | |
defmodule Operator do | |
def first <~> last do | |
DateRange.new first, last | |
end | |
end | |
defmodule Helpers do | |
def date_to_gregorian_days date do | |
date |> Date.to_erl |> :calendar.date_to_gregorian_days | |
end | |
def gregorian_days_to_date int do | |
{:ok, date} = int |> :calendar.gregorian_days_to_date |> Date.from_erl | |
date | |
end | |
end | |
def new(first, last) do | |
d1 = Helpers.date_to_gregorian_days first | |
d2 = Helpers.date_to_gregorian_days last | |
%DateRange{first: first, last: last, first_gregorian_days: d1, last_greogorian_days: d2} | |
end | |
defimpl Enumerable, for: DateRange do | |
def reduce(%DateRange{first_gregorian_days: d1, last_greogorian_days: d2}, acc, fun) do | |
reduce(d1, d2, acc, fun, d1 <= d2) | |
end | |
defp reduce(_x, _y, {:halt, acc}, _fun, _up) do | |
{:halted, acc} | |
end | |
defp reduce(x, y, {:suspend, acc}, fun, up) do | |
{:suspended, acc, &reduce(x, y, &1, fun, up)} | |
end | |
defp reduce(x, y, {:cont, acc}, fun, true) when x <= y do # increase | |
d = Helpers.gregorian_days_to_date x # NOTE it yeilds a date, not an integer | |
reduce(x + 1, y, fun.(d, acc), fun, true) | |
end | |
defp reduce(x, y, {:cont, acc}, fun, false) when x >= y do # decrease | |
d = Helpers.gregorian_days_to_date x # NOTE it yeilds a date, not an integer | |
reduce(x - 1, y, fun.(d, acc), fun, false) | |
end | |
defp reduce(_, _, {:cont, acc}, _fun, _up) do | |
{:done, acc} | |
end | |
def member?(%DateRange{first_gregorian_days: d1, last_greogorian_days: d2}, value) do | |
val = Helpers.date_to_gregorian_days value | |
{:ok, Enum.member?(d1..d2, val)} | |
end | |
def count(%DateRange{first_gregorian_days: d1, last_greogorian_days: d2}) do | |
if d1 <= d2 do | |
{:ok, d2 - d1 + 1} | |
else | |
{:ok, d1 - d2 + 1} | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's borrowed from the Elixir Range