Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ Add holidays to your list of dependencies in `mix.exs`:
3. Call the `on` function, passing in a date and a list of regions.

```
iex> Holidays.on({2016, 1, 1}, [:us])
iex> Holidays.on(~D[2016-01-01], [:us])
[%{name: "New Year's Day"}]
```

The `on` function gives a list of holidays for a date within
specified regions.

Dates in Erlang (and therefore Elixir) are represented by the
`{year, month, day}` tuple.
Dates in Elixir are represented by the
`~D[yyyy-mm-dd]` sigil.

Regions are often country codes, like `:us` or `:ca`, but
may also be entities such as UPS (`:ups`) or the New York Stock Exchange
Expand Down
4 changes: 2 additions & 2 deletions lib/holidays.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ defmodule Holidays do

## Examples

iex> Holidays.on({2016, 1, 1}, [:us])
iex> Holidays.on(~D[2016-01-01], [:us])
[%{name: "New Year's Day"}]

"""
@spec on(:calendar.date(), [region]) :: list
@spec on(Calendar.date(), [region]) :: list
def on(date, regions) do
Holidays.Define.on(date, regions)
end
Expand Down
54 changes: 19 additions & 35 deletions lib/holidays/date_calculator/date_math.ex
Original file line number Diff line number Diff line change
@@ -1,55 +1,38 @@
defmodule Holidays.DateCalculator.DateMath do
@doc """
Adds the given number of `days` to the given `date`.

## Examples

iex> Holidays.DateCalculator.DateMath.add_days({2015, 12, 31}, 1)
{2016, 1, 1}

iex> Holidays.DateCalculator.DateMath.add_days({2016, 1, 6}, -12)
{2015, 12, 25}

"""
@spec add_days(:calendar.date(), integer) :: :calendar.date()
def add_days(date, days) do
:calendar.gregorian_days_to_date(:calendar.date_to_gregorian_days(date) + days)
end

@offset %{:first => 1, :second => 8, :third => 15, :fourth => 22}

@doc """
Returns the date for the `week`th `weekday` for the given `year` and `month`.

`week` may be one of :first, :second, :third, :fourth, :last

`weekday` may be a number between 1 and 7, which is the way Erlang
`weekday` may be a number between 1 and 7, which is the way Elixir
represents Monday through Sunday. Or use one the atoms
:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday

## Examples

# The second Tuesday of June, 2013
iex> Holidays.DateCalculator.DateMath.get_weekth_day(2013, 6, :second, :tuesday)
{2013, 6, 11}
~D[2013-06-11]

# The third Friday of December, 2013
iex> Holidays.DateCalculator.DateMath.get_weekth_day(2013, 12, :third, :friday)
{2013, 12, 20}
~D[2013-12-20]

# The last Saturday of January, 2013
iex> Holidays.DateCalculator.DateMath.get_weekth_day(2013, 1, :last, :saturday)
{2013, 1, 26}
~D[2013-01-26]

"""
@spec get_weekth_day(
pos_integer,
pos_integer,
Holidays.week(),
Holidays.weekday() | pos_integer
) :: :calendar.date()
) :: Date.t()
def get_weekth_day(year, month, :last, weekday) do
offset = :calendar.last_day_of_the_month(year, month) - 6
offset = Date.days_in_month(Date.new!(year, month, 1)) - 6
do_get_weekth_day(year, month, offset, weekday)
end

Expand All @@ -68,22 +51,23 @@ defmodule Holidays.DateCalculator.DateMath do
}

@spec do_get_weekth_day(pos_integer, pos_integer, pos_integer, Holidays.weekday() | pos_integer) ::
:calendar.date()
Date.t()
defp do_get_weekth_day(year, month, offset, weekday) when not is_integer(weekday) do
do_get_weekth_day(year, month, offset, @daynum[weekday])
end

defp do_get_weekth_day(year, month, offset, weekday) do
day = weekday - :calendar.day_of_the_week(year, month, offset) + offset
date = Date.new!(year, month, offset)
day = weekday - Date.day_of_week(date) + offset
correct_offset(year, month, offset, day)
end

@spec correct_offset(pos_integer, pos_integer, pos_integer, integer) :: :calendar.date()
@spec correct_offset(pos_integer, pos_integer, pos_integer, integer) :: Date.t()
defp correct_offset(year, month, offset, day) when day < offset do
{year, month, day + 7}
Date.new!(year, month, day + 7)
end

defp correct_offset(year, month, _offset, day), do: {year, month, day}
defp correct_offset(year, month, _offset, day), do: Date.new!(year, month, day)

@dayname %{
1 => :monday,
Expand All @@ -103,22 +87,22 @@ defmodule Holidays.DateCalculator.DateMath do

## Examples

iex> Holidays.DateCalculator.DateMath.get_week_and_weekday({2016,1,29})
iex> Holidays.DateCalculator.DateMath.get_week_and_weekday(~D[2016-01-29])
[{:last, :friday}]

iex> Holidays.DateCalculator.DateMath.get_week_and_weekday({2016,1,25})
iex> Holidays.DateCalculator.DateMath.get_week_and_weekday(~D[2016-01-25])
[{:fourth, :monday}, {:last, :monday}]

iex> Holidays.DateCalculator.DateMath.get_week_and_weekday({2016,1,5})
iex> Holidays.DateCalculator.DateMath.get_week_and_weekday(~D[2016-01-05])
[{:first, :tuesday}]
"""
@spec get_week_and_weekday(:calendar.date()) :: [{Holidays.week(), Holidays.weekday()}]
def get_week_and_weekday({year, month, day} = date) do
day_name = @dayname[:calendar.day_of_the_week(date)]
@spec get_week_and_weekday(Date.t()) :: [{Holidays.week(), Holidays.weekday()}]
def get_week_and_weekday(%Date{day: day} = date) do
day_name = @dayname[Date.day_of_week(date)]

week_name(div(day - 1, 7), day_name) ++
check_last_week(
:calendar.last_day_of_the_month(year, month) - day,
Date.days_in_month(date) - day,
day_name
)
end
Expand Down
19 changes: 8 additions & 11 deletions lib/holidays/date_calculator/easter.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
defmodule Holidays.DateCalculator.Easter do
alias Holidays.DateCalculator.DateMath

@doc ~S"""
Returns the date of Easter for the given `year`

## Examples

iex> Holidays.DateCalculator.Easter.gregorian_easter_for(2016)
{2016, 3, 27}
~D[2016-03-27]

iex> Holidays.DateCalculator.Easter.gregorian_easter_for(2015)
{2015, 4, 5}
~D[2015-04-05]

"""
def gregorian_easter_for(year) do
Expand All @@ -31,23 +29,22 @@ defmodule Holidays.DateCalculator.Easter do
month = (h + l - 7 * m + 114) |> div(31)
day = ((h + l - 7 * m + 114) |> rem(31)) + 1

{year, month, day}
Date.new!(year, month, day)
end

@doc ~S"""

Returns the date of Orthodox Easter in the given `year`

## Examples

iex> Holidays.DateCalculator.Easter.gregorian_orthodox_easter_for(2016)
{2016, 5, 1}
~D[2016-05-01]

iex> Holidays.DateCalculator.Easter.gregorian_orthodox_easter_for(1815)
{1815, 4, 30}
~D[1815-04-30]

iex> Holidays.DateCalculator.Easter.gregorian_orthodox_easter_for(2101)
{2101, 4, 24}
~D[2101-04-24]

"""
def gregorian_orthodox_easter_for(year) do
Expand All @@ -69,7 +66,7 @@ defmodule Holidays.DateCalculator.Easter do
0
end

DateMath.add_days(j_date, offset)
Date.add(j_date, offset)
end

def julian_orthodox_easter_for(year) do
Expand All @@ -80,6 +77,6 @@ defmodule Holidays.DateCalculator.Easter do
j_month = 3 + div(i - j + 40, 44)
j_day = i - j + 28 - 31 * div(j_month, 4)

{year, j_month, j_day}
Date.new!(year, j_month, j_day)
end
end
54 changes: 26 additions & 28 deletions lib/holidays/date_calculator/weekend_modifier.ex
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
defmodule Holidays.DateCalculator.WeekendModifier do
alias Holidays.DateCalculator.DateMath

@doc """
Move `date` to Monday if it occurs on a Saturday or Sunday.

## Examples

iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_weekend({2015,12,5})
{2015,12,7}
iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_weekend(~D[2015-12-05])
~D[2015-12-07]

iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_weekend({2015,12,6})
{2015,12,7}
iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_weekend(~D[2015-12-06])
~D[2015-12-07]

iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_weekend({2015,12,8})
{2015,12,8}
iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_weekend(~D[2015-12-08])
~D[2015-12-08]

"""
def to_monday_if_weekend(date) do
case :calendar.day_of_the_week(date) do
7 -> DateMath.add_days(date, 1)
6 -> DateMath.add_days(date, 2)
case Date.day_of_week(date) do
6 -> Date.add(date, 2)
7 -> Date.add(date, 1)
_ -> date
end
end
Expand All @@ -29,19 +27,19 @@ defmodule Holidays.DateCalculator.WeekendModifier do

## Examples

iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_sunday({2015,12,5})
{2015,12,5}
iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_sunday(~D[2015-12-05])
~D[2015-12-05]

iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_sunday({2015,12,6})
{2015,12,7}
iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_sunday(~D[2015-12-06])
~D[2015-12-07]

iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_sunday({2015,12,8})
{2015,12,8}
iex> Holidays.DateCalculator.WeekendModifier.to_monday_if_sunday(~D[2015-12-08])
~D[2015-12-08]

"""
def to_monday_if_sunday(date) do
case :calendar.day_of_the_week(date) do
7 -> DateMath.add_days(date, 1)
case Date.day_of_week(date) do
7 -> Date.add(date, 1)
_ -> date
end
end
Expand All @@ -56,20 +54,20 @@ defmodule Holidays.DateCalculator.WeekendModifier do

## Examples

iex> Holidays.DateCalculator.WeekendModifier.to_weekday_if_weekend({2015,12,5})
{2015,12,4}
iex> Holidays.DateCalculator.WeekendModifier.to_weekday_if_weekend(~D[2015-12-05])
~D[2015-12-04]

iex> Holidays.DateCalculator.WeekendModifier.to_weekday_if_weekend({2015,12,6})
{2015,12,7}
iex> Holidays.DateCalculator.WeekendModifier.to_weekday_if_weekend(~D[2015-12-06])
~D[2015-12-07]

iex> Holidays.DateCalculator.WeekendModifier.to_weekday_if_weekend({2015,12,8})
{2015,12,8}
iex> Holidays.DateCalculator.WeekendModifier.to_weekday_if_weekend(~D[2015-12-08])
~D[2015-12-08]

"""
def to_weekday_if_weekend(date) do
case :calendar.day_of_the_week(date) do
7 -> DateMath.add_days(date, 1)
6 -> DateMath.add_days(date, -1)
case Date.day_of_week(date) do
7 -> Date.add(date, 1)
6 -> Date.add(date, -1)
_ -> date
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/holidays/define.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule Holidays.Define do
GenServer.cast(__MODULE__, {:add_entry, :fun, {name, function, regions}})
end

@spec on(:calendar.date(), [Holidays.region()]) :: list
@spec on(Date.t(), [Holidays.region()]) :: list
def on(date, regions) do
GenServer.call(__MODULE__, {:on, date, regions})
end
Expand All @@ -30,7 +30,7 @@ defmodule Holidays.Define do
on_fun(funs, date)
end

defp on_static(holidays, {_, month, day}) do
defp on_static(holidays, %Date{month: month, day: day}) do
holidays
|> Enum.filter(fn
{_, ^month, ^day, _} -> true
Expand All @@ -44,7 +44,7 @@ defmodule Holidays.Define do
|> Enum.flat_map(&on_nth(&1, holidays, date))
end

defp on_nth({week, weekday}, holidays, {_, month, _}) do
defp on_nth({week, weekday}, holidays, %Date{month: month}) do
holidays
|> Enum.filter(&match?({_, ^month, ^week, ^weekday, _}, &1))
|> Enum.map(fn {name, _, _, _, regions} -> %{name: name, regions: regions} end)
Expand All @@ -58,10 +58,10 @@ defmodule Holidays.Define do

defp apply_fun({mod, fun, args, days}, date) do
apply_fun({mod, fun, args}, date)
|> DateMath.add_days(days)
|> Date.add(days)
end

defp apply_fun({mod, fun, [:year]}, {year, _, _}) do
defp apply_fun({mod, fun, [:year]}, %Date{year: year}) do
apply(mod, fun, [year])
end

Expand Down
2 changes: 1 addition & 1 deletion lib/holidays/definitions/br.ex
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ defmodule Holidays.Definitions.Br do
:none

iex> Holidays.Definitions.Br.election_day(2018)
{2018, 10, 7}
~D[2018-10-07]
"""
def election_day(year) when rem(year, 4) == 2,
do: DateMath.get_weekth_day(year, 10, :first, :sunday)
Expand Down
10 changes: 7 additions & 3 deletions lib/holidays/definitions/de.ex
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,16 @@ defmodule Holidays.Definitions.De do
end

def buss_und_bettag(year) do
wday = rem(:calendar.day_of_the_week(year, 11, 23), 7)
wday =
year
|> Date.new!(11, 23)
|> Date.day_of_week()
|> rem(7)

if wday > 3 do
{year, 11, 23 - (wday - 3)}
Date.new!(year, 11, 23 - (wday - 3))
else
{year, 11, 23 - (wday + 4)}
Date.new!(year, 11, 23 - (wday + 4))
end
end
end
6 changes: 3 additions & 3 deletions lib/holidays/definitions/us.ex
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,16 @@ defmodule Holidays.Definitions.Us do
:none

iex> Holidays.Definitions.Us.inauguration_day(2017)
{2017, 1, 20}
~D[2017-01-20]

iex> Holidays.Definitions.Us.inauguration_day(2018)
:none
"""
def inauguration_day(year) when rem(year, 4) == 1, do: {year, 1, 20}
def inauguration_day(year) when rem(year, 4) == 1, do: Date.new!(year, 1, 20)
def inauguration_day(_year), do: :none

def day_after_thanksgiving(year) do
DateMath.get_weekth_day(year, 11, :fourth, :thursday)
|> DateMath.add_days(1)
|> Date.add(1)
end
end
Loading