Skip to content

Instantly share code, notes, and snippets.

@Jeweller-Tsai
Created August 6, 2016 10:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jeweller-Tsai/49ec7b14bc0d42a122ac262f6f9b5477 to your computer and use it in GitHub Desktop.
Save Jeweller-Tsai/49ec7b14bc0d42a122ac262f6f9b5477 to your computer and use it in GitHub Desktop.
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
@Jeweller-Tsai
Copy link
Author

import DateRange.Operator

d0 = ~D[2015-12-28]
d1 = ~D[2016-01-10]

d0 <~> d1
# => %DateRange{first: ~D[2015-12-28], first_gregorian_days: 736325, last: ~D[2016-01-10], last_greogorian_days: 736338}

@Jeweller-Tsai
Copy link
Author

It's borrowed from the Elixir Range

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment