Packages
timex
2.1.1
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/datetime.ex
defimpl Timex.Comparable, for: Timex.DateTime do
alias Timex.Time
alias Timex.DateTime
alias Timex.AmbiguousDateTime
alias Timex.Comparable
alias Timex.Convertable
alias Timex.Types
import Timex.Macros
@units [:years, :months, :weeks, :calendar_weeks, :days, :hours, :minutes, :seconds, :timestamp]
@doc """
See docs for `Timex.compare/3`
"""
@spec compare(DateTime.t, Comparable.comparable, Comparable.granularity) :: Comparable.compare_result
def compare(a, :epoch, granularity), do: compare(a, DateTime.epoch(), granularity)
def compare(a, :zero, granularity), do: compare(a, DateTime.zero(), granularity)
def compare(_, :distant_past, _granularity), do: +1
def compare(_, :distant_future, _granularity), do: -1
def compare(a, a, _granularity), do: 0
def compare(_, %AmbiguousDateTime{} = b, _granularity),
do: {:error, {:ambiguous_comparison, b}}
def compare(%DateTime{} = this, %DateTime{} = other, granularity) when granularity in @units do
case {ok!(DateTime.to_seconds(this, :zero)), ok!(DateTime.to_seconds(other, :zero))} do
{{:error, _} = err, _} ->
err
{_, {:error, _} = err} ->
err
{{:ok, this_secs}, {:ok, other_secs}} ->
case ok!(diff(this, other, granularity)) do
{:error, _} = err ->
err
{:ok, delta} ->
occurs_after? = cond do
other_secs < this_secs -> true
:else -> false
end
cond do
delta == 0 -> 0
delta > 0 && occurs_after? -> 1
:else -> -1
end
end
end
end
def compare(_, _, granularity) when not granularity in @units,
do: {:error, {:invalid_granularity, granularity}}
def compare(a, b, granularity) do
case Convertable.to_datetime(b) do
{:error, _} = err ->
err
%DateTime{} = datetime ->
compare(a, datetime, granularity)
%AmbiguousDateTime{} = ambiguous ->
{:error, {:ambiguous_comparison, ambiguous}}
end
end
@doc """
See docs for `Timex.compare/3`
"""
@spec diff(DateTime.t, Comparable.comparable, Comparable.granularity) :: Types.timestamp | integer | {:error, term}
def diff(a, b, :secs) do
IO.write :stderr, "warning: :secs is a deprecated unit name, use :seconds instead\n"
diff(a, b, :seconds)
end
def diff(a, b, :mins) do
IO.write :stderr, "warning: :mins is a deprecated unit name, use :minutes instead\n"
diff(a, b, :minutes)
end
def diff(_, %AmbiguousDateTime{} = b, _granularity),
do: {:error, {:ambiguous_comparison, b}}
def diff(%DateTime{} = this, %DateTime{} = other, type) do
case {ok!(DateTime.to_seconds(this, :zero)), ok!(DateTime.to_seconds(other, :zero))} do
{{:error, _} = err, _} ->
err
{_, {:error, _} = err} ->
err
{{:ok, this_secs}, {:ok, other_secs}} ->
diff_secs = this_secs - other_secs
cond do
diff_secs == 0 -> 0
diff_secs > 0 -> do_diff(this, this_secs, other, other_secs, type)
diff_secs < 0 -> do_diff(other, other_secs, this, this_secs, type)
end
end
end
# Handle custom conversions
def diff(a, b, granularity) do
case Convertable.to_datetime(b) do
{:error, _} = err ->
err
%DateTime{} = datetime ->
diff(a, datetime, granularity)
%AmbiguousDateTime{} = ambiguous ->
{:error, {:ambiguous_comparison, ambiguous}}
end
end
defp do_diff(_, a, _, a, _), do: 0
defp do_diff(_adate, a, _bdate, b, :timestamp) do
seconds = a - b
case ok!(Time.from(seconds, :seconds)) do
{:error, _} = err -> err
{:ok, timestamp} -> timestamp
end
end
defp do_diff(_adate, a, _bdate, b, :seconds), do: a - b
defp do_diff(_adate, a, _bdate, b, :minutes), do: div(a - b, 60)
defp do_diff(adate, a, bdate, b, :hours) do
minutes = do_diff(adate, a, bdate, b, :minutes)
div(minutes, 60)
end
defp do_diff(%DateTime{:year => ay, :month => am, :day => ad}, _, %DateTime{:year => by, :month => bm, :day => bd}, _, :days) do
a_days = :calendar.date_to_gregorian_days({ay,am,ad})
b_days = :calendar.date_to_gregorian_days({by,bm,bd})
a_days - b_days
end
defp do_diff(adate, a, bdate, b, :weeks) do
days = do_diff(adate, a, bdate, b, :days)
weeks = div(days, 7)
extra_days = rem(days, 7)
actual_weeks = (if extra_days == 0, do: weeks, else: weeks + 1)
cond do
actual_weeks == 1 && extra_days < 7 -> 0
:else -> actual_weeks
end
end
defp do_diff(adate, _, bdate, _, :calendar_weeks) do
case {ok!(Timex.end_of_week(adate)), ok!(Timex.beginning_of_week(bdate))} do
{{:error, _} = err, _} -> err
{_, {:error, _} = err} -> err
{{:ok, ending}, {:ok, start}} ->
end_secs = DateTime.to_seconds(ending, :zero)
start_secs = DateTime.to_seconds(start, :zero)
days = do_diff(ending, end_secs, start, start_secs, :days)
weeks = div(days, 7)
extra_days = rem(days, 7)
actual_weeks = (if extra_days == 0, do: weeks, else: weeks + 1)
result = cond do
actual_weeks == 1 && extra_days < 7 -> 0
:else -> actual_weeks
end
result
end
end
defp do_diff(%DateTime{:year => ly, :month => lm, :day => ld}, _, %DateTime{:year => ey, :month => em, :day => ed}, _, :months) do
x = cond do
ld >= ed -> 0
:else -> -1
end
y = ly - ey
z = lm - em
x+y*12+z
end
defp do_diff(adate, a, bdate, b, :years) do
months = do_diff(adate, a, bdate, b, :months)
years = div(months, 12)
years
end
defp do_diff(_, _, _, _, unit) when not unit in @units,
do: {:error, {:invalid_granularity, unit}}
end