Packages
timex
3.1.20
3.7.13
3.7.12
3.7.11
3.7.9
3.7.8
3.7.7
3.7.6
3.7.5
3.7.3
3.7.2
3.7.1
3.7.0
3.6.4
3.6.3
3.6.2
retired
3.6.1
3.6.0
3.5.0
3.4.2
3.4.1
3.3.0
3.2.2
3.2.1
3.2.0
3.1.25
retired
3.1.24
3.1.23
3.1.22
3.1.21
3.1.20
3.1.19
3.1.18
3.1.17
3.1.16
3.1.15
3.1.13
3.1.12
3.1.11
3.1.10
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.2.1
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.0
1.0.2
1.0.1
1.0.0
1.0.0-rc4
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
1.0.0-pre
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.0
0.10.2
0.10.1
0.10.0
0.9.0
0.8.0
0.7.1
0.6.0
0.5.0
0.4.8
0.4.7
0.4.6
Timex is a rich, comprehensive Date/Time library for Elixir projects, with full timezone support via the :tzdata package. If you need to manipulate dates, times, datetimes, timestamps, etc., then Timex is for you!
Current section
Files
Jump to
Current section
Files
lib/comparable/diff.ex
defmodule Timex.Comparable.Diff do
@moduledoc false
alias Timex.Types
alias Timex.Duration
alias Timex.Comparable
@units [:years, :months, :weeks, :calendar_weeks, :days,
:hours, :minutes, :seconds, :milliseconds, :microseconds,
:duration]
@spec diff(Types.microseconds, Types.microseconds, Comparable.granularity) :: integer
def diff(a, a, granularity) when is_integer(a), do: zero(granularity)
def diff(a, b, granularity) when is_integer(a) and is_integer(b) and is_atom(granularity) do
do_diff(a, b, granularity)
end
defp do_diff(a, a, type), do: zero(type)
defp do_diff(a, b, :duration), do: Duration.from_seconds(do_diff(a,b,:seconds))
defp do_diff(a, b, :microseconds), do: a - b
defp do_diff(a, b, :milliseconds), do: div(a - b, 1_000)
defp do_diff(a, b, :seconds), do: div(a - b, 1_000*1_000)
defp do_diff(a, b, :minutes), do: div(a - b, 1_000*1_000*60)
defp do_diff(a, b, :hours), do: div(a - b, 1_000*1_000*60*60)
defp do_diff(a, b, :days), do: div(a - b, 1_000*1_000*60*60*24)
defp do_diff(a, b, :weeks), do: div(a - b, 1_000*1_000*60*60*24*7)
defp do_diff(a, b, :calendar_weeks) do
adate = :calendar.gregorian_seconds_to_datetime(div(a, 1_000*1_000))
bdate = :calendar.gregorian_seconds_to_datetime(div(b, 1_000*1_000))
days = cond do
a > b ->
ending = Timex.end_of_week(adate)
start = Timex.beginning_of_week(bdate)
endu = Timex.to_gregorian_microseconds(ending)
startu = Timex.to_gregorian_microseconds(start)
do_diff(endu, startu, :days)
:else ->
ending = Timex.end_of_week(bdate)
start = Timex.beginning_of_week(adate)
endu = Timex.to_gregorian_microseconds(ending)
startu = Timex.to_gregorian_microseconds(start)
do_diff(startu, endu, :days)
end
cond do
days >= 0 && rem(days, 7) != 0 -> div(days, 7) + 1
days <= 0 && rem(days, 7) != 0 -> div(days, 7) - 1
:else -> div(days, 7)
end
end
defp do_diff(a, b, :months) do
diff_months(a, b)
end
defp do_diff(a, b, :years) do
diff_years(a, b)
end
defp do_diff(_, _, granularity) when not granularity in @units,
do: {:error, {:invalid_granularity, granularity}}
defp zero(:duration), do: Duration.zero
defp zero(_type), do: 0
defp diff_years(a, a), do: 0
defp diff_years(a, b) do
{start_date, _} = :calendar.gregorian_seconds_to_datetime(div(a, 1_000*1_000))
{end_date, _} = :calendar.gregorian_seconds_to_datetime(div(b, 1_000*1_000))
if a > b do
diff_years(end_date, start_date, 0)
else
diff_years(start_date, end_date, 0) * -1
end
end
defp diff_years({y, _, _}, {y, _, _}, acc) do
acc
end
defp diff_years({y1, m, d}, {y2, _, _} = ed, acc) when y1 < y2 do
sd2 = {y1+1, m, d}
if :calendar.valid_date(sd2) do
sd2_secs = :calendar.datetime_to_gregorian_seconds({sd2,{0,0,0}})
ed_secs = :calendar.datetime_to_gregorian_seconds({ed,{0,0,0}})
if sd2_secs <= ed_secs do
diff_years(sd2, ed, acc+1)
else
acc
end
else
# This date is a leap day, so subtract a day and try again
diff_years({y1, m, d-1}, ed, acc)
end
end
defp diff_months(a, a), do: 0
defp diff_months(a, b) do
{start_date, _} = :calendar.gregorian_seconds_to_datetime(div(a, 1_000*1_000))
{end_date, _} = :calendar.gregorian_seconds_to_datetime(div(b, 1_000*1_000))
if a > b do
do_diff_months(end_date, start_date)
else
do_diff_months(start_date, end_date) * -1
end
end
defp do_diff_months({y,m,_}, {y,m,_}), do: 0
defp do_diff_months({y1, m1, d1}, {y2, m2, d2}) when y1 <= y2 and m1 < m2 do
year_diff = y2 - y1
month_diff = if d2 >= d1, do: m2 - m1, else: (m2-1)-m1
(year_diff*12)+month_diff
end
defp do_diff_months({y1,m1,d1}, {y2,m2,d2}) when y1 < y2 and m1 > m2 do
year_diff = y2 - (y1+1)
month_diff =
cond do
d2 == d1 ->
12 - (m1-m2)
d2 > d1 ->
12 - ((m1-1)-m2)
d2 < d1 ->
12 - (m1-m2)
end
(year_diff*12)+month_diff
end
defp do_diff_months({y1,m,d1}, {y2,m,d2}) when y1 < y2 do
year_diff = y2 - (y1+1)
month_diff =
cond do
d2 > d1 ->
11
:else ->
12
end
(year_diff*12)+month_diff
end
end