Packages
timex
0.14.2
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/time/time.ex
defmodule Timex.Time do
@usecs_in_sec 1_000_000
@usecs_in_msec 1_000
@msecs_in_sec 1_000
@secs_in_min 60
@secs_in_hour @secs_in_min * 60
@secs_in_day @secs_in_hour * 24
@secs_in_week @secs_in_day * 7
@million 1_000_000
@type units :: :usecs | :msecs | :secs | :mins | :hours | :days | :weeks | :hms
@type quantity :: float
Enum.each [usecs: @usecs_in_sec, msecs: @msecs_in_sec], fn {type, coef} ->
@spec to_usecs(quantity, unquote(type)) :: quantity
def to_usecs(value, unquote(type)), do: value * @usecs_in_sec / unquote(coef)
@spec to_msecs(quantity, unquote(type)) :: quantity
def to_msecs(value, unquote(type)), do: value * @msecs_in_sec / unquote(coef)
@spec to_secs(quantity, unquote(type)) :: quantity
def to_secs(value, unquote(type)), do: value / unquote(coef)
@spec to_mins(quantity, unquote(type)) :: quantity
def to_mins(value, unquote(type)), do: value / unquote(coef) / @secs_in_min
@spec to_hours(quantity, unquote(type)) :: quantity
def to_hours(value, unquote(type)), do: value / unquote(coef) / @secs_in_hour
@spec to_days(quantity, unquote(type)) :: quantity
def to_days(value, unquote(type)), do: value / unquote(coef) / @secs_in_day
@spec to_weeks(quantity, unquote(type)) :: quantity
def to_weeks(value, unquote(type)), do: value / unquote(coef) / @secs_in_week
end
Enum.each [secs: 1, mins: @secs_in_min, hours: @secs_in_hour, days: @secs_in_day, weeks: @secs_in_week], fn {type, coef} ->
@spec unquote(type)(quantity) :: quantity
def unquote(type)(value), do: value * unquote(coef)
@spec to_usecs(quantity, unquote(type)) :: quantity
def to_usecs(value, unquote(type)), do: value * unquote(coef) * @usecs_in_sec
@spec to_msecs(quantity, unquote(type)) :: quantity
def to_msecs(value, unquote(type)), do: value * unquote(coef) * @msecs_in_sec
@spec to_secs(quantity, unquote(type)) :: quantity
def to_secs(value, unquote(type)), do: value * unquote(coef)
@spec to_mins(quantity, unquote(type)) :: quantity
def to_mins(value, unquote(type)), do: value * unquote(coef) / @secs_in_min
@spec to_hours(quantity, unquote(type)) :: quantity
def to_hours(value, unquote(type)), do: value * unquote(coef) / @secs_in_hour
@spec to_days(quantity, unquote(type)) :: quantity
def to_days(value, unquote(type)), do: value * unquote(coef) / @secs_in_day
@spec to_weeks(quantity, unquote(type)) :: quantity
def to_weeks(value, unquote(type)), do: value * unquote(coef) / @secs_in_week
end
Enum.each [:to_usecs, :to_msecs, :to_secs, :to_mins, :to_hours, :to_days, :to_weeks], fn name ->
@spec unquote(name)({quantity, quantity, quantity}, :hms) :: quantity
def unquote(name)({hours, minutes, seconds}, :hms), do: unquote(name)(hours * @secs_in_hour + minutes * @secs_in_min + seconds, :secs)
end
@doc """
Converts an hour between 0..24 to {1..12, :am/:pm}
## Examples
iex> to_12hour_clock(23)
{11, :pm}
"""
def to_12hour_clock(hour) when hour in 0..24 do
case hour do
hour when hour in [0, 24] -> {12, :am}
hour when hour < 12 -> {hour, :am}
hour when hour === 12 -> {12, :pm}
hour when hour > 12 -> {hour - 12, :pm}
end
end
@doc """
Converts an hour between 1..12 in either am or pm, to value between 0..24
## Examples
iex> to_24hour_clock(7, :pm)
19
"""
def to_24hour_clock(hour, am_or_pm) when hour in 1..12 and am_or_pm in [:am, :pm] do
case am_or_pm do
:am when hour === 12 -> 0
:am -> hour
:pm when hour === 12 -> hour
:pm -> hour + 12
end
end
def from(value, :usecs) do
value = round(value)
{ sec, micro } = mdivmod(value)
{ mega, sec } = mdivmod(sec)
{ mega, sec, micro }
end
def from(value, :msecs), do: from(value * @usecs_in_msec, :usecs)
def from(value, :secs), do: from(value * @usecs_in_sec, :usecs)
def from(value, :mins), do: from(value * @secs_in_min, :secs)
def from(value, :hours), do: from(value * @secs_in_hour, :secs)
def from(value, :days), do: from(value * @secs_in_day, :secs)
def from(value, :weeks), do: from(value * @secs_in_week, :secs)
def from(value, :hms), do: from(to_secs(value, :hms), :secs)
@spec to_usecs({quantity, quantity, quantity}) :: quantity
def to_usecs({mega, sec, micro}), do: (mega * @million + sec) * @million + micro
@spec to_msecs({quantity, quantity, quantity}) :: quantity
def to_msecs({_, _, _} = ts), do: to_usecs(ts) / @usecs_in_msec
@spec to_secs({quantity, quantity, quantity}) :: quantity
def to_secs({_, _, _} = ts), do: to_usecs(ts) / @usecs_in_sec
@spec to_mins({quantity, quantity, quantity}) :: quantity
def to_mins(timestamp), do: to_secs(timestamp) / @secs_in_min
@spec to_hours({quantity, quantity, quantity}) :: quantity
def to_hours(timestamp), do: to_secs(timestamp) / @secs_in_hour
@spec to_days({quantity, quantity, quantity}) :: quantity
def to_days(timestamp), do: to_secs(timestamp) / @secs_in_day
@spec to_weeks({quantity, quantity, quantity}) :: quantity
def to_weeks(timestamp), do: to_secs(timestamp) / @secs_in_week
Enum.each [:usecs, :msecs, :secs, :mins, :hours, :days, :weeks, :hms], fn type ->
def to_timestamp(value, unquote(type)), do: from(value, unquote(type))
end
def add({mega1,sec1,micro1}, {mega2,sec2,micro2}) do
normalize { mega1+mega2, sec1+sec2, micro1+micro2 }
end
def sub({mega1,sec1,micro1}, {mega2,sec2,micro2}) do
normalize { mega1-mega2, sec1-sec2, micro1-micro2 }
end
def scale({mega, secs, micro}, coef) do
normalize { mega*coef, secs*coef, micro*coef }
end
def invert({mega, sec, micro}) do
{ -mega, -sec, -micro }
end
def abs(timestamp={mega, sec, micro}) do
cond do
mega != 0 -> value = mega
sec != 0 -> value = sec
true -> value = micro
end
if value < 0 do
invert(timestamp)
else
timestamp
end
end
@doc """
Return a timestamp representing a time lapse of length 0.
Time.convert(Time.zero, :secs)
#=> 0
Can be useful for operations on collections of timestamps. For instance,
Enum.reduce timestamps, Time.zero, Time.add(&1, &2)
"""
def zero, do: {0, 0, 0}
@doc """
Convert timestamp in the form { megasecs, seconds, microsecs } to the
specified time units.
Supported units: microseconds (:usecs), milliseconds (:msecs), seconds (:secs),
minutes (:mins), hours (:hours), days (:days), or weeks (:weeks).
"""
def convert(timestamp, type \\ :timestamp)
def convert(timestamp, :timestamp), do: timestamp
def convert(timestamp, :usecs), do: to_usecs(timestamp)
def convert(timestamp, :msecs), do: to_msecs(timestamp)
def convert(timestamp, :secs), do: to_secs(timestamp)
def convert(timestamp, :mins), do: to_mins(timestamp)
def convert(timestamp, :hours), do: to_hours(timestamp)
def convert(timestamp, :days), do: to_days(timestamp)
def convert(timestamp, :weeks), do: to_weeks(timestamp)
@doc """
Return time interval since the first day of year 0 to Epoch.
"""
def epoch(type \\ :timestamp)
def epoch(:timestamp) do
seconds = :calendar.datetime_to_gregorian_seconds({ {1970,1,1}, {0,0,0} })
{ mega, sec } = mdivmod(seconds)
{ mega, sec, 0 }
end
def epoch(type) do
convert(epoch, type)
end
@doc """
Time interval since Epoch.
The argument is an atom indicating the type of time units to return (see
convert/2 for supported values).
When the argument is omitted, the return value's format is { megasecs, seconds, microsecs }.
"""
def now(type \\ :timestamp)
def now(:timestamp) do
:os.timestamp
end
def now(type) do
convert(now, type)
end
@doc """
Time interval between timestamp and now. If timestamp is after now in time, the
return value will be negative. Timestamp must be in format { megasecs, seconds,
microseconds }.
The second argument is an atom indicating the type of time units to return:
microseconds (:usecs), milliseconds (:msecs), seconds (:secs), minutes (:mins),
or hours (:hours).
When the second argument is omitted, the return value's format is { megasecs,
seconds, microsecs }.
"""
def elapsed(timestamp, type \\ :timestamp)
def elapsed(timestamp = {_,_,_}, type) do
elapsed(timestamp, now, type)
end
def elapsed(timestamp = {_,_,_}, reference_time = {_,_,_}, type) do
diff(reference_time, timestamp) |> convert(type)
end
@doc """
Time interval between two timestamps. If the first timestamp comes before the
second one in time, the return value will be negative. Timestamp must be in format
{ megasecs, seconds, microseconds }.
The third argument is an atom indicating the type of time units to return:
microseconds (:usecs), milliseconds (:msecs), seconds (:secs), minutes (:mins),
or hours (:hours).
When the third argument is omitted, the return value's format is { megasecs,
seconds, microsecs }.
"""
def diff(t1, t2, type \\ :timestamp)
def diff({mega1,secs1,micro1}, {mega2,secs2,micro2}, :timestamp) do
# TODO: normalize the result
{mega1 - mega2, secs1 - secs2, micro1 - micro2}
end
def diff(t1 = {_,_,_}, t2 = {_,_,_}, type) do
convert(diff(t1, t2), type)
end
@doc """
Evaluates fun() and measures the elapsed time as reported by :os.timestamp/0. Returns {time, value}, where time is { megasecs, seconds, microsecs } and value is what is returned from the function evaluation.
## Example
iex> Time.measure(fn -> 2 * 2 end)
{{0, 0, 10}, 4}
"""
@spec measure((() -> any)) :: { Date.timestamp, any }
def measure(fun) do
measure_result(:timer.tc(fun))
end
@doc """
Evaluates apply(fun, args). Otherwise works like measure/1
"""
@spec measure(fun, [any]) :: { Date.timestamp, any }
def measure(fun, args) do
measure_result(:timer.tc(fun, args))
end
@doc """
Evaluates apply(module, fun, args). Otherwise works like measure/1
"""
@spec measure(module, atom, [any]) :: { Date.timestamp, any }
def measure(module, fun, args) do
measure_result(:timer.tc(module, fun, args))
end
defp measure_result({micro, ret}) do
{ to_timestamp(micro, :usecs), ret }
end
defp normalize({mega, sec, micro}) do
# TODO: check for negative values
if micro >= @million do
{ sec, micro } = mdivmod(sec, micro)
end
if sec >= @million do
{ mega, sec } = mdivmod(mega, sec)
end
{ mega, sec, micro }
end
defp divmod(a, b) do
{ div(a, b), rem(a, b) }
end
defp divmod(initial, a, b) do
{ initial + div(a, b), rem(a, b) }
end
defp mdivmod(a) do
divmod(a, 1_000_000)
end
defp mdivmod(initial, a) do
divmod(initial, a, 1_000_000)
end
end