Packages
timex
3.6.4
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/duration.ex
defmodule Timex.Duration do
@moduledoc """
This module provides a friendly API for working with Erlang
timestamps, i.e. `{megasecs, secs, microsecs}`. In addition,
it provides an easy way to wrap the measurement of function
execution time (via `measure`).
"""
alias __MODULE__
alias Timex.Types
use Timex.Constants
@enforce_keys [:megaseconds, :seconds, :microseconds]
defstruct megaseconds: 0, seconds: 0, microseconds: 0
@type t :: %__MODULE__{
megaseconds: integer,
seconds: integer,
microseconds: integer
}
@type units :: :microseconds | :milliseconds | :seconds | :minutes | :hours | :days | :weeks
@type measurement_units :: :microseconds | :milliseconds | :seconds | :minutes | :hours
@type to_options :: [truncate: boolean]
@doc """
Converts a Duration to an Erlang timestamp
## Example
iex> d = %Timex.Duration{megaseconds: 1, seconds: 2, microseconds: 3}
...> Timex.Duration.to_erl(d)
{1, 2, 3}
"""
@spec to_erl(__MODULE__.t()) :: Types.timestamp()
def to_erl(%__MODULE__{} = d),
do: {d.megaseconds, d.seconds, d.microseconds}
@doc """
Converts an Erlang timestamp to a Duration
## Example
iex> Timex.Duration.from_erl({1, 2, 3})
%Timex.Duration{megaseconds: 1, seconds: 2, microseconds: 3}
"""
@spec from_erl(Types.timestamp()) :: __MODULE__.t()
def from_erl({mega, sec, micro}),
do: %__MODULE__{megaseconds: mega, seconds: sec, microseconds: micro}
@doc """
Converts a Duration to a Time if the duration fits within a 24-hour clock.
If it does not, an error tuple is returned.
## Examples
iex> d = %Timex.Duration{megaseconds: 0, seconds: 4000, microseconds: 0}
...> Timex.Duration.to_time(d)
{:ok, ~T[01:06:40]}
iex> d = %Timex.Duration{megaseconds: 1, seconds: 0, microseconds: 0}
...> Timex.Duration.to_time(d)
{:error, :invalid_time}
"""
@spec to_time(__MODULE__.t()) :: {:ok, Time.t()} | {:error, atom}
def to_time(%__MODULE__{} = d) do
{h, m, s, us} = to_clock(d)
Time.from_erl({h, m, s}, Timex.DateTime.Helpers.construct_microseconds(us, -1))
end
@doc """
Same as to_time/1, but returns the Time directly. Raises an error if the
duration does not fit within a 24-hour clock.
## Examples
iex> d = %Timex.Duration{megaseconds: 0, seconds: 4000, microseconds: 0}
...> Timex.Duration.to_time!(d)
~T[01:06:40]
iex> d = %Timex.Duration{megaseconds: 1, seconds: 0, microseconds: 0}
...> Timex.Duration.to_time!(d)
** (ArgumentError) cannot convert {277, 46, 40} to time, reason: :invalid_time
"""
@spec to_time!(__MODULE__.t()) :: Time.t() | no_return
def to_time!(%__MODULE__{} = d) do
{h, m, s, us} = to_clock(d)
Time.from_erl!({h, m, s}, Timex.DateTime.Helpers.construct_microseconds(us, -1))
end
@doc """
Converts a Time to a Duration
## Example
iex> Timex.Duration.from_time(~T[01:01:30])
%Timex.Duration{megaseconds: 0, seconds: 3690, microseconds: 0}
"""
@spec from_time(Time.t()) :: __MODULE__.t()
def from_time(%Time{} = t) do
{us, _} = t.microsecond
from_clock({t.hour, t.minute, t.second, us})
end
@doc """
Converts a Duration to a string, using the ISO standard for formatting durations.
## Examples
iex> d = %Timex.Duration{megaseconds: 0, seconds: 3661, microseconds: 0}
...> Timex.Duration.to_string(d)
"PT1H1M1S"
iex> d = %Timex.Duration{megaseconds: 102, seconds: 656013, microseconds: 33}
...> Timex.Duration.to_string(d)
"P3Y3M3DT3H33M33.000033S"
"""
@spec to_string(__MODULE__.t()) :: String.t()
def to_string(%__MODULE__{} = duration) do
Timex.Format.Duration.Formatter.format(duration)
end
@doc """
Parses a duration string (in ISO-8601 format) into a Duration struct.
"""
@spec parse(String.t()) :: {:ok, __MODULE__.t()} | {:error, term}
defdelegate parse(str), to: Timex.Parse.Duration.Parser
@doc """
Parses a duration string into a Duration struct, using the provided parser module.
"""
@spec parse(String.t(), module()) :: {:ok, __MODULE__.t()} | {:error, term}
defdelegate parse(str, module), to: Timex.Parse.Duration.Parser
@doc """
Same as parse/1, but returns the Duration unwrapped, and raises on error
"""
@spec parse!(String.t()) :: __MODULE__.t() | no_return
defdelegate parse!(str), to: Timex.Parse.Duration.Parser
@doc """
Same as parse/2, but returns the Duration unwrapped, and raises on error
"""
@spec parse!(String.t(), module()) :: __MODULE__.t() | no_return
defdelegate parse!(str, module), to: Timex.Parse.Duration.Parser
@doc """
Converts a Duration to a clock tuple, i.e. `{hour,minute,second,microsecond}`.
## Example
iex> d = %Timex.Duration{megaseconds: 1, seconds: 1, microseconds: 50}
...> Timex.Duration.to_clock(d)
{277, 46, 41, 50}
"""
def to_clock(%__MODULE__{megaseconds: mega, seconds: sec, microseconds: micro}) do
ss = mega * 1_000_000 + sec
ss =
cond do
micro > 1_000_000 -> ss + div(micro, 1_000_000)
:else -> ss
end
hour = div(ss, 60 * 60)
min = div(rem(ss, 60 * 60), 60)
secs = rem(rem(ss, 60 * 60), 60)
{hour, min, secs, rem(micro, 1_000_000)}
end
@doc """
Converts a clock tuple, i.e. `{hour, minute, second, microsecond}` to a Duration.
## Example
iex> Timex.Duration.from_clock({1, 2, 3, 4})
%Timex.Duration{megaseconds: 0, seconds: 3723, microseconds: 4}
"""
def from_clock({hour, minute, second, usec}) do
total_seconds = hour * 60 * 60 + minute * 60 + second
mega = div(total_seconds, 1_000_000)
ss = rem(total_seconds, 1_000_000)
from_erl({mega, ss, usec})
end
@doc """
Converts a Duration to its value in microseconds
## Example
iex> Duration.to_microseconds(Duration.from_milliseconds(10.5))
10_500
"""
@spec to_microseconds(__MODULE__.t()) :: integer
@spec to_microseconds(__MODULE__.t(), to_options) :: integer
def to_microseconds(%Duration{megaseconds: mega, seconds: sec, microseconds: micro}) do
total_seconds = mega * @million + sec
total_microseconds = total_seconds * 1_000 * 1_000 + micro
total_microseconds
end
def to_microseconds(%Duration{} = duration, _opts), do: to_microseconds(duration)
@doc """
Converts a Duration to its value in milliseconds
## Example
iex> Duration.to_milliseconds(Duration.from_seconds(1))
1000.0
iex> Duration.to_milliseconds(Duration.from_seconds(1.543))
1543.0
iex> Duration.to_milliseconds(Duration.from_seconds(1.543), truncate: true)
1543
"""
@spec to_milliseconds(__MODULE__.t()) :: float
@spec to_milliseconds(__MODULE__.t(), to_options) :: float | integer
def to_milliseconds(%__MODULE__{} = d), do: to_microseconds(d) / 1_000
def to_milliseconds(%__MODULE__{} = d, truncate: true), do: trunc(to_milliseconds(d))
def to_milliseconds(%__MODULE__{} = d, _opts), do: to_milliseconds(d)
@doc """
Converts a Duration to its value in seconds
## Example
iex> Duration.to_seconds(Duration.from_milliseconds(1500))
1.5
iex> Duration.to_seconds(Duration.from_milliseconds(1500), truncate: true)
1
"""
@spec to_seconds(__MODULE__.t()) :: float
@spec to_seconds(__MODULE__.t(), to_options) :: float | integer
def to_seconds(%__MODULE__{} = d), do: to_microseconds(d) / (1_000 * 1_000)
def to_seconds(%__MODULE__{} = d, truncate: true), do: trunc(to_seconds(d))
def to_seconds(%__MODULE__{} = d, _opts), do: to_seconds(d)
@doc """
Converts a Duration to its value in minutes
## Example
iex> Duration.to_minutes(Duration.from_seconds(90))
1.5
iex> Duration.to_minutes(Duration.from_seconds(65), truncate: true)
1
"""
@spec to_minutes(__MODULE__.t()) :: float
@spec to_minutes(__MODULE__.t(), to_options) :: float | integer
def to_minutes(%__MODULE__{} = d), do: to_microseconds(d) / (1_000 * 1_000 * 60)
def to_minutes(%__MODULE__{} = d, truncate: true), do: trunc(to_minutes(d))
def to_minutes(%__MODULE__{} = d, _opts), do: to_minutes(d)
@doc """
Converts a Duration to its value in hours
## Example
iex> Duration.to_hours(Duration.from_minutes(105))
1.75
iex> Duration.to_hours(Duration.from_minutes(105), truncate: true)
1
"""
@spec to_hours(__MODULE__.t()) :: float
@spec to_hours(__MODULE__.t(), to_options) :: float | integer
def to_hours(%__MODULE__{} = d), do: to_microseconds(d) / (1_000 * 1_000 * 60 * 60)
def to_hours(%__MODULE__{} = d, truncate: true), do: trunc(to_hours(d))
def to_hours(%__MODULE__{} = d, _opts), do: to_hours(d)
@doc """
Converts a Duration to its value in days
## Example
iex> Duration.to_days(Duration.from_hours(6))
0.25
iex> Duration.to_days(Duration.from_hours(25), truncate: true)
1
"""
@spec to_days(__MODULE__.t()) :: float
@spec to_days(__MODULE__.t(), to_options) :: float | integer
def to_days(%__MODULE__{} = d), do: to_microseconds(d) / (1_000 * 1_000 * 60 * 60 * 24)
def to_days(%__MODULE__{} = d, truncate: true), do: trunc(to_days(d))
def to_days(%__MODULE__{} = d, _opts), do: to_days(d)
@doc """
Converts a Duration to its value in weeks
## Example
iex> Duration.to_weeks(Duration.from_days(14))
2.0
iex> Duration.to_weeks(Duration.from_days(13), truncate: true)
1
"""
@spec to_weeks(__MODULE__.t()) :: float
@spec to_weeks(__MODULE__.t(), to_options) :: float | integer
def to_weeks(%__MODULE__{} = d), do: to_microseconds(d) / (1_000 * 1_000 * 60 * 60 * 24 * 7)
def to_weeks(%__MODULE__{} = d, truncate: true), do: trunc(to_weeks(d))
def to_weeks(%__MODULE__{} = d, _opts), do: to_weeks(d)
Enum.each(
[
{:microseconds, 1 / @usecs_in_sec},
{:milliseconds, 1 / @msecs_in_sec},
{:seconds, 1},
{:minutes, @secs_in_min},
{:hours, @secs_in_hour},
{:days, @secs_in_day},
{:weeks, @secs_in_week}
],
fn {type, coef} ->
@spec to_microseconds(integer | float, unquote(type)) :: float
def to_microseconds(value, unquote(type)),
do: do_round(value * unquote(coef) * @usecs_in_sec)
@spec to_milliseconds(integer | float, unquote(type)) :: float
def to_milliseconds(value, unquote(type)),
do: do_round(value * unquote(coef) * @msecs_in_sec)
@spec to_seconds(integer | float, unquote(type)) :: float
def to_seconds(value, unquote(type)),
do: do_round(value * unquote(coef))
@spec to_minutes(integer | float, unquote(type)) :: float
def to_minutes(value, unquote(type)),
do: do_round(value * unquote(coef) / @secs_in_min)
@spec to_hours(integer | float, unquote(type)) :: float
def to_hours(value, unquote(type)),
do: do_round(value * unquote(coef) / @secs_in_hour)
@spec to_days(integer | float, unquote(type)) :: float
def to_days(value, unquote(type)),
do: do_round(value * unquote(coef) / @secs_in_day)
@spec to_weeks(integer | float, unquote(type)) :: float
def to_weeks(value, unquote(type)),
do: do_round(value * unquote(coef) / @secs_in_week)
end
)
@doc """
Converts an integer value representing microseconds to a Duration
"""
@spec from_microseconds(integer) :: __MODULE__.t()
def from_microseconds(us) do
us = round(us)
{sec, micro} = mdivmod(us)
{mega, sec} = mdivmod(sec)
%Duration{megaseconds: mega, seconds: sec, microseconds: micro}
end
@doc """
Converts an integer value representing milliseconds to a Duration
"""
@spec from_milliseconds(integer) :: __MODULE__.t()
def from_milliseconds(ms), do: from_microseconds(ms * @usecs_in_msec)
@doc """
Converts an integer value representing seconds to a Duration
"""
@spec from_seconds(integer) :: __MODULE__.t()
def from_seconds(s), do: from_microseconds(s * @usecs_in_sec)
@doc """
Converts an integer value representing minutes to a Duration
"""
@spec from_minutes(integer) :: __MODULE__.t()
def from_minutes(m), do: from_seconds(m * @secs_in_min)
@doc """
Converts an integer value representing hours to a Duration
"""
@spec from_hours(integer) :: __MODULE__.t()
def from_hours(h), do: from_seconds(h * @secs_in_hour)
@doc """
Converts an integer value representing days to a Duration
"""
@spec from_days(integer) :: __MODULE__.t()
def from_days(d), do: from_seconds(d * @secs_in_day)
@doc """
Converts an integer value representing weeks to a Duration
"""
@spec from_weeks(integer) :: __MODULE__.t()
def from_weeks(w), do: from_seconds(w * @secs_in_week)
@doc """
Add one Duration to another.
## Examples
iex> d = %Timex.Duration{megaseconds: 1, seconds: 1, microseconds: 1}
...> Timex.Duration.add(d, d)
%Timex.Duration{megaseconds: 2, seconds: 2, microseconds: 2}
iex> d = %Timex.Duration{megaseconds: 1, seconds: 750000, microseconds: 750000}
...> Timex.Duration.add(d, d)
%Timex.Duration{megaseconds: 3, seconds: 500001, microseconds: 500000}
"""
@spec add(__MODULE__.t(), __MODULE__.t()) :: __MODULE__.t()
def add(
%Duration{megaseconds: mega1, seconds: sec1, microseconds: micro1},
%Duration{megaseconds: mega2, seconds: sec2, microseconds: micro2}
) do
normalize(%Duration{
megaseconds: mega1 + mega2,
seconds: sec1 + sec2,
microseconds: micro1 + micro2
})
end
@doc """
Subtract one Duration from another.
## Example
iex> d1 = %Timex.Duration{megaseconds: 3, seconds: 3, microseconds: 3}
...> d2 = %Timex.Duration{megaseconds: 2, seconds: 2, microseconds: 2}
...> Timex.Duration.sub(d1, d2)
%Timex.Duration{megaseconds: 1, seconds: 1, microseconds: 1}
"""
@spec sub(__MODULE__.t(), __MODULE__.t()) :: __MODULE__.t()
def sub(
%Duration{megaseconds: mega1, seconds: sec1, microseconds: micro1},
%Duration{megaseconds: mega2, seconds: sec2, microseconds: micro2}
) do
normalize(%Duration{
megaseconds: mega1 - mega2,
seconds: sec1 - sec2,
microseconds: micro1 - micro2
})
end
@doc """
Scale a Duration by some coefficient value, i.e. a scale of 2 is twice is long.
## Example
iex> d = %Timex.Duration{megaseconds: 1, seconds: 1, microseconds: 1}
...> Timex.Duration.scale(d, 2)
%Timex.Duration{megaseconds: 2, seconds: 2, microseconds: 2}
"""
@spec scale(__MODULE__.t(), coefficient :: integer | float) :: __MODULE__.t()
def scale(%Duration{megaseconds: mega, seconds: secs, microseconds: micro}, coef) do
mega_s = mega * coef
s_diff = mega_s * 1_000_000 - trunc(mega_s) * 1_000_000
secs_s = s_diff + secs * coef
us_diff = secs_s * 1_000_000 - trunc(secs_s) * 1_000_000
us_s = us_diff + micro * coef
extra_mega = div(trunc(secs_s), 1_000_000)
mega_final = trunc(mega_s) + extra_mega
extra_secs = div(trunc(us_s), 1_000_000)
secs_final = trunc(secs_s) - extra_mega * 1_000_000 + extra_secs
us_final = trunc(us_s) - extra_secs * 1_000_000
normalize(%Duration{megaseconds: mega_final, seconds: secs_final, microseconds: us_final})
end
@doc """
Invert a Duration, i.e. a positive duration becomes a negative one, and vice versa
## Example
iex> d = %Timex.Duration{megaseconds: -1, seconds: -2, microseconds: -3}
...> Timex.Duration.invert(d)
%Timex.Duration{megaseconds: 1, seconds: 2, microseconds: 3}
"""
@spec invert(__MODULE__.t()) :: __MODULE__.t()
def invert(%Duration{megaseconds: mega, seconds: sec, microseconds: micro}) do
%Duration{megaseconds: -mega, seconds: -sec, microseconds: -micro}
end
@doc """
Returns the absolute value of the provided Duration.
## Example
iex> d = %Timex.Duration{megaseconds: -1, seconds: -2, microseconds: -3}
...> Timex.Duration.abs(d)
%Timex.Duration{megaseconds: 1, seconds: 2, microseconds: 3}
"""
@spec abs(__MODULE__.t()) :: __MODULE__.t()
def abs(%Duration{} = duration) do
us = to_microseconds(duration)
if us < 0 do
from_microseconds(-us)
else
duration
end
end
@doc """
Return a timestamp representing a time lapse of length 0.
iex> Timex.Duration.zero |> Timex.Duration.to_seconds
0.0
Can be useful for operations on collections of durations. For instance,
Enum.reduce(durations, Duration.zero, Duration.add(&1, &2))
Can also be used to represent the timestamp of the start of the UNIX epoch,
as all Erlang timestamps are relative to this point.
"""
@spec zero() :: __MODULE__.t()
def zero, do: %Duration{megaseconds: 0, seconds: 0, microseconds: 0}
@doc """
Returns the duration since the first day of year 0 to Epoch.
## Example
iex> Timex.Duration.epoch()
%Timex.Duration{megaseconds: 62_167, seconds: 219_200, microseconds: 0}
"""
@spec epoch() :: __MODULE__.t()
def epoch() do
epoch(nil)
end
@doc """
Returns the amount of time since the first day of year 0 to Epoch.
The argument is an atom indicating the type of time units to return.
The allowed unit type atoms are:
- :microseconds
- :milliseconds
- :seconds
- :minutes
- :hours
- :days
- :weeks
## Examples
iex> Timex.Duration.epoch(:seconds)
62_167_219_200
If the specified type is nil, a duration since the first day of year 0 to Epoch
is returned.
iex> Timex.Duration.epoch(nil)
%Timex.Duration{megaseconds: 62_167, seconds: 219_200, microseconds: 0}
"""
@spec epoch(nil) :: __MODULE__.t()
@spec epoch(units) :: non_neg_integer
def epoch(type) do
seconds = :calendar.datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}})
case type do
nil ->
from_seconds(seconds)
:microseconds ->
seconds |> from_seconds |> to_microseconds
:milliseconds ->
seconds |> from_seconds |> to_milliseconds
:seconds ->
seconds
:minutes ->
seconds |> from_seconds |> to_minutes
:hours ->
seconds |> from_seconds |> to_hours
:days ->
seconds |> from_seconds |> to_days
:weeks ->
seconds |> from_seconds |> to_weeks
end
end
@doc """
Returns the amount of time since Epoch.
The argument is an atom indicating the type of time units to return.
The allowed unit type atoms are:
- :microseconds
- :milliseconds
- :seconds
- :minutes
- :hours
- :days
- :weeks
## Examples
iex> Timex.Duration.now(:seconds)
1483141644
When the argument is omitted or nil, a Duration is returned.
iex> Timex.Duration.now
%Timex.Duration{megaseconds: 1483, seconds: 141562, microseconds: 536938}
"""
@spec now() :: __MODULE__.t()
@spec now(nil) :: __MODULE__.t()
@spec now(units) :: non_neg_integer
def now(type \\ nil)
def now(nil), do: :os.system_time(:micro_seconds) |> from_microseconds
def now(:microseconds), do: :os.system_time(:micro_seconds)
def now(:milliseconds), do: :os.system_time(:milli_seconds)
def now(:seconds), do: :os.system_time(:seconds)
def now(:minutes), do: :os.system_time(:seconds) |> from_seconds |> to_minutes
def now(:hours), do: :os.system_time(:seconds) |> from_seconds |> to_hours
def now(:days), do: :os.system_time(:seconds) |> from_seconds |> to_days
def now(:weeks), do: :os.system_time(:seconds) |> from_seconds |> to_weeks
@doc """
An alias for `Duration.diff/3`
"""
defdelegate elapsed(duration, ref \\ nil, type \\ nil), to: __MODULE__, as: :diff
@doc """
This function determines the difference in time between two timestamps
(represented by Duration structs). If the second timestamp is omitted,
`Duration.now` will be used as the reference timestamp. If the first
timestamp argument occurs before the second, the resulting measurement will
be a negative value.
The type argument is an atom indicating the units the measurement should be
returned in. If no type argument is provided, a Duration will be returned.
Valid measurement units for this function are:
:microseconds, :milliseconds, :seconds, :minutes, :hours, :days, or :weeks
## Examples
iex> alias Timex.Duration
...> d = Duration.from_erl({1457, 136000, 785000})
...> Duration.diff(d, Duration.zero, :days)
16865
"""
def diff(t1, t2, type \\ nil)
def diff(%Duration{} = t1, nil, type), do: diff(t1, now(), type)
def diff(%Duration{} = t1, %Duration{} = t2, type) do
delta = do_diff(t1, t2)
case type do
nil -> delta
:microseconds -> to_microseconds(delta, truncate: true)
:milliseconds -> to_milliseconds(delta, truncate: true)
:seconds -> to_seconds(delta, truncate: true)
:minutes -> to_minutes(delta, truncate: true)
:hours -> to_hours(delta, truncate: true)
:days -> to_days(delta, truncate: true)
:weeks -> to_weeks(delta, truncate: true)
end
end
defp do_diff(%Duration{} = t1, %Duration{} = t2) do
microsecs = :timer.now_diff(to_erl(t1), to_erl(t2))
mega = div(microsecs, 1_000_000_000_000)
secs = div(microsecs - mega * 1_000_000_000_000, 1_000_000)
micro = rem(microsecs, 1_000_000)
%Duration{megaseconds: mega, seconds: secs, microseconds: micro}
end
@doc """
Evaluates fun() and measures the elapsed time.
Returns `{Duration.t, result}`.
## Example
iex> {_timestamp, result} = Duration.measure(fn -> 2 * 2 end)
...> result == 4
true
"""
@spec measure((() -> any)) :: {__MODULE__.t(), any}
def measure(fun) when is_function(fun) do
{time, result} = :timer.tc(fun, [])
{Duration.from_microseconds(time), result}
end
@doc """
Evaluates `apply(fun, args)`, and measures execution time.
Returns `{Duration.t, result}`.
## Example
iex> {_timestamp, result} = Duration.measure(fn x, y -> x * y end, [2, 4])
...> result == 8
true
"""
@spec measure(fun, [any]) :: {__MODULE__.t(), any}
def measure(fun, args) when is_function(fun) and is_list(args) do
{time, result} = :timer.tc(fun, args)
{Duration.from_microseconds(time), result}
end
@doc """
Evaluates `apply(module, fun, args)`, and measures execution time.
Returns `{Duration.t, result}`.
## Example
iex> {_timestamp, result} = Duration.measure(Enum, :map, [[1,2], &(&1*2)])
...> result == [2, 4]
true
"""
@spec measure(module, atom, [any]) :: {__MODULE__.t(), any}
def measure(module, fun, args)
when is_atom(module) and is_atom(fun) and is_list(args) do
{time, result} = :timer.tc(module, fun, args)
{Duration.from_microseconds(time), result}
end
def normalize(%Duration{megaseconds: mega, seconds: sec, microseconds: micro}) do
# TODO: check for negative values
{sec, micro} = mdivmod(sec, micro)
{mega, sec} = mdivmod(mega, sec)
%Duration{megaseconds: mega, seconds: sec, microseconds: micro}
end
defp divmod(a, b), do: {div(a, b), rem(a, b)}
defp divmod(initial, a, b), do: {initial + div(a, b), rem(a, b)}
defp mdivmod(a), do: divmod(a, 1_000_000)
defp mdivmod(initial, a), do: divmod(initial, a, 1_000_000)
defp do_round(value) when is_integer(value), do: value
defp do_round(value) when is_float(value), do: Float.round(value, 6)
end