Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/timezone support #15

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

config :recurring_events, date_helper_module: RecurringEvents.DateTimex
# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
Expand Down
20 changes: 11 additions & 9 deletions lib/recurring_events/by_checker.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
defmodule RecurringEvents.ByChecker do
alias RecurringEvents.{Date, Guards}
alias RecurringEvents.Guards
use Guards

@date_helper Application.get_env(:recurring_events, :date_helper_module)

@rules [
:by_month,
:by_month_day,
Expand Down Expand Up @@ -61,24 +63,24 @@ defmodule RecurringEvents.ByChecker do
end

defp is_year_day(date, number) when number > 0 do
Date.day_of_the_year(date) == number
@date_helper.day_of_the_year(date) == number
end

defp is_year_day(%{year: year} = date, number) when number < 0 do
last_day = if(:calendar.is_leap_year(year), do: 366, else: 365)
-(last_day - Date.day_of_the_year(date) + 1) == number
-(last_day - @date_helper.day_of_the_year(date) + 1) == number
end

def is_week_no_in(date, %{by_week_number: numbers, week_start: week_start}) do
Enum.any?(numbers, &is_week_no_eq(date, &1, week_start))
end

defp is_week_no_eq(date, number, week_start) when number > 0 do
Date.week_number(date, week_start: week_start) == number
@date_helper.week_number(date, week_start: week_start) == number
end

defp is_week_no_eq(date, number, week_start) when number < 0 do
Date.week_number(date, reversed: true, week_start: week_start) == number
@date_helper.week_number(date, reversed: true, week_start: week_start) == number
end

defp is_month_day_in(date, %{by_month_day: days}) do
Expand All @@ -90,7 +92,7 @@ defmodule RecurringEvents.ByChecker do
end

defp is_month_day_eq(%{day: date_day} = date, day) when day < 0 do
date_day - (Date.last_day_of_the_month(date) + 1) == day
date_day - (@date_helper.last_day_of_the_month(date) + 1) == day
end

defp is_month_in(date, %{by_month: months}) do
Expand Down Expand Up @@ -118,15 +120,15 @@ defmodule RecurringEvents.ByChecker do
end

defp is_week_day_eq(date, week_day) do
Date.week_day(date) == week_day
@date_helper.week_day(date) == week_day
end

defp is_week_day_eq(date, {n, _} = week_day, period) when n < 0 do
Date.numbered_week_day(date, period, :backward) == week_day
@date_helper.numbered_week_day(date, period, :backward) == week_day
end

defp is_week_day_eq(date, {n, _} = week_day, period) when n > 0 do
Date.numbered_week_day(date, period, :foreward) == week_day
@date_helper.numbered_week_day(date, period, :foreward) == week_day
end

defp is_week_day_eq(date, week_day, _period) when is_atom(week_day) do
Expand Down
37 changes: 24 additions & 13 deletions lib/recurring_events/by_pump.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
defmodule RecurringEvents.ByPump do
alias RecurringEvents.{Date, Frequency, Guards}

alias RecurringEvents.{Frequency, Guards}
use Guards

@date_helper Application.get_env(:recurring_events, :date_helper_module)

@rules [
:by_month,
:by_month_day,
Expand Down Expand Up @@ -61,18 +62,25 @@ defmodule RecurringEvents.ByPump do
defp inflate_week(date, rules) do
week_start = week_start_date(date, rules)
week_end = week_end_date(date, rules)

inflate_period(week_start, week_end)
end

defp inflate_month(date) do
month_start = %{date | day: 1}
month_end = %{date | day: Date.last_day_of_the_month(date)}
start_shift = 1 - date.day
end_shift = @date_helper.last_day_of_the_month(date) -1

month_start = @date_helper.shift_date(date, start_shift, :days)
month_end = @date_helper.shift_date(month_start, end_shift, :days)
inflate_period(month_start, month_end)
end

defp inflate_year(date) do
year_start = %{date | day: 1, month: 1}
year_end = %{date | day: 31, month: 12}
start_shift = 1 - @date_helper.day_of_the_year(date)
end_shift = @date_helper.last_day_of_the_year(date) - 1

year_start = @date_helper.shift_date(date, start_shift, :days)
year_end = @date_helper.shift_date(year_start, end_shift, :days)
inflate_period(year_start, year_end)
end

Expand All @@ -95,38 +103,41 @@ defmodule RecurringEvents.ByPump do

defp inflate_by_month(date, %{by_month: months}) do
Stream.map(months, fn month ->
day = Date.last_day_of_the_month(%{date | month: month})
%{date | month: month, day: min(day, date.day)}
day = @date_helper.last_day_of_the_month(%{date | month: month})
month_shift = month - date.month
new_date = date |> @date_helper.shift_date(month_shift, :months)
day_shift = min(day, date.day) - new_date.day
new_date |> @date_helper.shift_date(day_shift, :days)
end)
end

defp week_start_date(date, rules) do
current_day = Date.week_day(date)
current_day = @date_helper.week_day(date)
start_day = week_start_day(rules)

if current_day == start_day do
date
else
date
|> Date.shift_date(-1, :days)
|> @date_helper.shift_date(-1, :days)
|> week_start_date(rules)
end
end

defp week_end_date(date, rules) do
current_day = Date.week_day(date)
current_day = @date_helper.week_day(date)
end_day = week_end_day(rules)

if current_day == end_day do
date
else
date
|> Date.shift_date(1, :days)
|> @date_helper.shift_date(1, :days)
|> week_end_date(rules)
end
end

defp week_end_day(%{week_start: start_day}), do: Date.prev_week_day(start_day)
defp week_end_day(%{week_start: start_day}), do: @date_helper.prev_week_day(start_day)
defp week_end_day(%{}), do: :sunday

defp week_start_day(%{week_start: start_day}), do: start_day
Expand Down
24 changes: 21 additions & 3 deletions lib/recurring_events/date.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ defmodule RecurringEvents.Date do
end

@doc """
Returns last daty of the month for provided date.
Returns last day of the month for provided date.

# Example

Expand All @@ -135,6 +135,25 @@ defmodule RecurringEvents.Date do
:calendar.last_day_of_the_month(year, month)
end

@doc """
Returns last day of the year for provided date.

# Example

iex> RecurringEvents.Date.last_day_of_the_year(~D[2017-02-04])
365
iex> RecurringEvents.Date.last_day_of_the_year(~D[2016-02-04])
366

"""
def last_day_of_the_year(%{year: year}) do
if(:calendar.is_leap_year(year), do: 366, else: 365)
end

def last_day_of_the_year({year, _month, _day}) do
if(:calendar.is_leap_year(year), do: 366, else: 365)
end

@doc """
Returns week day of provided date

Expand Down Expand Up @@ -191,8 +210,7 @@ defmodule RecurringEvents.Date do

def numbered_week_day({year, _month, _day} = date, :year, :backward) do
day_of_the_week = week_day(date)
last_day = if(:calendar.is_leap_year(year), do: 366, else: 365)
count = div(last_day - day_of_the_year(date), 7) + 1
count = div(last_day_of_the_year(date) - day_of_the_year(date), 7) + 1
{-count, day_of_the_week}
end

Expand Down
21 changes: 21 additions & 0 deletions lib/recurring_events/date_timex.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule RecurringEvents.DateTimex do
@moduledoc """
Helper module responsible for common date manipulations.
This one is using Timex if avaliable
"""

alias RecurringEvents.Date

def last_day_of_the_month(d), do: Date.last_day_of_the_month(d)
def last_day_of_the_year(d), do: Date.last_day_of_the_year(d)
def week_number(d, o), do: Date.week_number(d, o)
def numbered_week_day(d, p, o), do: Date.numbered_week_day(d, p, o)
def week_day(d), do: Date.week_day(d)
def day_of_the_year(d), do: Date.day_of_the_year(d)
def compare(d1, d2), do: Date.compare(d1, d2)
def prev_week_day(d), do: Date.prev_week_day(d)

def shift_date(date, count, period) do
Timex.shift(date, [{period, count}])
end
end
6 changes: 3 additions & 3 deletions lib/recurring_events/frequency.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule RecurringEvents.Frequency do
Handles `:frequency` frequency rule
"""

alias RecurringEvents.Date
@date_helper Application.get_env(:recurring_events, :date_helper_module)

@doc """
Returns frequency stream of dates with respect to `:interval`, `:count` and
Expand Down Expand Up @@ -48,15 +48,15 @@ defmodule RecurringEvents.Frequency do
defp get_step_by(:secondly), do: :seconds

defp next_iteration(date, step, iteration, step_by) do
next_date = Date.shift_date(date, step * iteration, step_by)
next_date = @date_helper.shift_date(date, step * iteration, step_by)
acc = {date, iteration + 1}
{[next_date], acc}
end

defp until_reached(_date, :forever), do: false

defp until_reached(date, until_date) do
Date.compare(date, until_date) == :gt
@date_helper.compare(date, until_date) == :gt
end

defp until_date(%{until: until_date}), do: until_date
Expand Down
8 changes: 4 additions & 4 deletions lib/recurring_events/monthly.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule RecurringEvents.Monthly do
Handles `:monthly` frequency rule
"""

alias RecurringEvents.Date
@date_helper Application.get_env(:recurring_events, :date_helper_module)

@doc """
Returns monthly stream of dates with respect to `:interval`, `:count` and
Expand Down Expand Up @@ -40,19 +40,19 @@ defmodule RecurringEvents.Monthly do
end

defp next_iteration(date, step, iteration) do
next_date = Date.shift_date(date, step * iteration, :months)
next_date = @date_helper.shift_date(date, step * iteration, :months)
acc = {date, iteration + 1}
{[next_date], acc}
end

defp until_reached(_date, :forever), do: false

defp until_reached(date, until_date) do
Date.compare(date, until_date) == :gt
@date_helper.compare(date, until_date) == :gt
end

defp until_date(%{until: until_date}) do
last_day = Date.last_day_of_the_month(until_date)
last_day = @date_helper.last_day_of_the_month(until_date)
%{until_date | day: last_day}
end

Expand Down
12 changes: 6 additions & 6 deletions lib/recurring_events/weekly.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule RecurringEvents.Weekly do
Handles `:weekly` frequency rule
"""

alias RecurringEvents.Date
@date_helper Application.get_env(:recurring_events, :date_helper_module)

@doc """
Returns weekly stream of dates with respect to `:interval`, `:count` and
Expand Down Expand Up @@ -41,15 +41,15 @@ defmodule RecurringEvents.Weekly do
end

defp next_iteration(date, step, iteration) do
next_date = Date.shift_date(date, step * iteration, :weeks)
next_date = @date_helper.shift_date(date, step * iteration, :weeks)
acc = {date, iteration + 1}
{[next_date], acc}
end

defp until_reached(_date, :forever), do: false

defp until_reached(date, until_date) do
Date.compare(date, until_date) == :gt
@date_helper.compare(date, until_date) == :gt
end

defp until_date(%{until: until_date} = rules) do
Expand All @@ -60,20 +60,20 @@ defmodule RecurringEvents.Weekly do
defp until_date(%{}), do: :forever

defp week_end_date(date, rules) do
current_day = Date.week_day(date)
current_day = @date_helper.week_day(date)
end_day = week_end_day(rules)

if current_day == end_day do
date
else
date
|> Date.shift_date(1, :days)
|> @date_helper.shift_date(1, :days)
|> week_end_date(rules)
end
end

defp week_end_day(%{week_start: start_day}) do
Date.prev_week_day(start_day)
@date_helper.prev_week_day(start_day)
end

defp week_end_day(%{}), do: :sunday
Expand Down
4 changes: 2 additions & 2 deletions lib/recurring_events/yearly.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule RecurringEvents.Yearly do
Handles `:yearly` frequency rule
"""

alias RecurringEvents.Date
@date_helper Application.get_env(:recurring_events, :date_helper_module)

@doc """
Returns yearly stream of dates with respect to `:interval`, `:count` and
Expand Down Expand Up @@ -41,7 +41,7 @@ defmodule RecurringEvents.Yearly do
end

defp next_iteration(date, step, iteration) do
next_date = Date.shift_date(date, step * iteration, :years)
next_date = @date_helper.shift_date(date, step * iteration, :years)
acc = {date, iteration + 1}
{[next_date], acc}
end
Expand Down
10 changes: 7 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule RecurringEvents.Mixfile do
def project do
[
app: :recurring_events,
version: "0.3.0",
version: "0.4.0",
elixir: "~> 1.5",
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
Expand All @@ -23,7 +23,7 @@ defmodule RecurringEvents.Mixfile do
#
# Type "mix help compile.app" for more information
def application do
[applications: [:logger]]
[applications: [:logger, :tzdata]]
end

# Dependencies can be Hex packages:
Expand All @@ -36,7 +36,11 @@ defmodule RecurringEvents.Mixfile do
#
# Type "mix help deps" for more examples and options
defp deps do
[{:ex_doc, ">= 0.0.0", only: :dev}, {:excoveralls, "~> 0.8", only: :test}]
[
{:ex_doc, ">= 0.0.0", only: :dev},
{:excoveralls, "~> 0.8", only: :test},
{:timex, "~> 3.1", optional: true}
]
end

defp package do
Expand Down
Loading