View Source Moar.Duration (Moar v1.62.0)

A duration is a {time, unit} tuple.

The time is a number and the unit is one of:

  • :nanosecond
  • :microsecond
  • :millisecond
  • :second
  • :minute
  • :hour
  • :day
  • :approx_month (30 days)
  • :approx_year (360 days)

Note

This module is naive and intentionally doesn't account for real-world calendars and all of their complexity, such as leap years, leap days, daylight saving time, past and future calendar oddities, etc.

As "Falsehoods programmers believe about time" says, "If you think you understand everything about time, you're probably doing it wrong."

See Cldr.Calendar.Duration for one example of a full-featured library that is far more likely to be correct.

Summary

Functions

Returns the duration between datetime and now, in the largest possible unit.

Shifts duration to an approximately equal duration that's simpler. For example, {121, :second} would get shifted to {2, :minute}.

Returns the duration between earlier and later, in the largest possible unit.

Converts a {duration, time_unit} tuple into a numeric duration, rounding down to the nearest whole number.

Formats a duration in either a long or short style, with optional transformers and an optional suffix.

Returns the duration between now and datetime, in the largest possible unit.

If possible, shifts duration to a higher time unit that is more readable to a human. Returns duration unchanged if it cannot be exactly shifted.

Shifts duration to time_unit. It is similar to convert/1 but this function returns a duration tuple, while convert/1 just returns an integer value.

Shifts duration to the next smaller unit. Raises if it's already at the smallest unit (nanosecond).

Shifts duration to the next larger unit. Raises if it's already at the largest unit (approx_year). Rounds the result towards zero.

Shortcut to format(duration, :long). See format/4.

Types

@type date_time_ish() :: DateTime.t() | NaiveDateTime.t() | binary()
@type format_style() :: :long | :short
@type format_transformer() :: :ago | :approx | :humanize
@type format_transformers() :: format_transformer() | [format_transformer()]
@type t() :: {time :: number(), unit :: time_unit()}
@type time_unit() ::
  :nanosecond
  | :microsecond
  | :millisecond
  | :second
  | :minute
  | :hour
  | :day
  | :approx_month
  | :approx_year

Functions

@spec ago(date_time_ish()) :: t()

Returns the duration between datetime and now, in the largest possible unit.

datetime can be an ISO8601-formatted string, a DateTime, or a NaiveDateTime.

See also from_now/1.

iex> DateTime.utc_now()
...> |> Moar.DateTime.subtract({121, :minute})
...> |> Moar.Duration.ago()
...> |> Moar.Duration.shift(:minute)
{121, :minute}
@spec approx(t()) :: t()

Shifts duration to an approximately equal duration that's simpler. For example, {121, :second} would get shifted to {2, :minute}.

Warning

This function is lossy because it intentionally loses precision.

If the time value of the duration is exactly 1, the duration is returned unchanged: {1, :minute} => {1, :minute}. Otherwise, the duration is shifted to the highest unit where the time value is >= 2.

iex> Moar.Duration.approx({1, :minute})
{1, :minute}

iex> Moar.Duration.approx({7300, :second})
{2, :hour}
@spec between(date_time_ish(), date_time_ish()) :: t()

Returns the duration between earlier and later, in the largest possible unit.

earlier and later can be ISO8601-formatted strings, DateTimes, or NaiveDateTimes.

iex> earlier = ~U[2020-01-01T00:00:00.000000Z]
iex> later = ~U[2020-01-01T02:01:00.000000Z]
iex> Moar.Duration.between(earlier, later)
{121, :minute}
Link to this function

convert(from_duration, to_unit)

View Source
@spec convert(from :: t(), to :: time_unit()) :: number()

Converts a {duration, time_unit} tuple into a numeric duration, rounding down to the nearest whole number.

Warning

This function is lossy because it rounds down to the nearest whole number.

Uses System.convert_time_unit/3 under the hood; see its documentation for more details.

It is similar to shift/1 but this function returns an integer value, while shift/1 returns a duration tuple.

iex> Moar.Duration.convert({121, :second}, :minute)
2
Link to this function

format(duration_or_datetime, style_or_transformers_or_suffix \\ nil, transformers_or_suffix \\ nil, suffix \\ nil)

View Source
@spec format(
  t() | date_time_ish(),
  format_style() | format_transformers() | binary() | nil,
  format_transformers() | binary() | nil,
  binary() | nil
) :: binary()

Formats a duration in either a long or short style, with optional transformers and an optional suffix.

  • The first argument is a duration tuple, unless one of the transformers is :ago, in which case it can be a DateTime, NaiveDateTime, or an ISO8601-formatted string.
  • The second argument is optional and is the style, transformer, list of transformers, or suffix.
  • The third argument is optional and is the transformer, list of transformers, or suffix.
  • The fourth argument is optional and is the suffix.

Styles:

  • :long, which formats like "25 seconds".
  • :short, which formats like "25s".
  • Defaults to :long

Transformers:

  • :ago transforms via ago/1
  • :approx transforms via approx/1
  • :humanize transforms via humanize/1
  • If no transformers are specified, no transformations are applied.

Suffix:

  • A string that will be appended to the formatted result.
  • If the :ago transformer is specified and a suffix is not specified, the suffix will default to "ago". To use the "ago" transformer with no suffix, specify an empty string as the suffix (nil will not suffice).
iex> Moar.Duration.format({1, :second})
"1 second"

iex> Moar.Duration.format({120, :second})
"120 seconds"

iex> Moar.Duration.format({120, :second}, :long)
"120 seconds"

iex> Moar.Duration.format({120, :second}, :short)
"120s"

iex> Moar.Duration.format({120, :second}, "yonder")
"120 seconds yonder"

iex> Moar.Duration.format({120, :second}, :humanize)
"2 minutes"

iex> Moar.Duration.format({120, :second}, :humanize, "yonder")
"2 minutes yonder"

iex> Moar.Duration.format({310, :second})
"310 seconds"

iex> Moar.Duration.format({310, :second}, :approx)
"5 minutes"

iex> DateTime.utc_now()
...> |> Moar.DateTime.add({-310, :second})
...> |> Moar.Duration.format(:short, [:ago, :approx], "henceforth")
"5m henceforth"
@spec from_now(date_time_ish()) :: t()

Returns the duration between now and datetime, in the largest possible unit.

datetime can be an ISO8601-formatted string, a DateTime, or a NaiveDateTime.

See also ago/1.

iex> DateTime.utc_now()
...> |> Moar.DateTime.add({121, :minute})
...> |> Moar.Duration.from_now()
...> |> Moar.Duration.approx()
{2, :hour}
@spec humanize(t()) :: t()

If possible, shifts duration to a higher time unit that is more readable to a human. Returns duration unchanged if it cannot be exactly shifted.

iex> Moar.Duration.humanize({60000, :millisecond})
{1, :minute}

iex> Moar.Duration.humanize({48, :hour})
{2, :day}

iex> Moar.Duration.humanize({49, :hour})
{49, :hour}
Link to this function

shift(duration, to_unit)

View Source
@spec shift(t(), time_unit()) :: t()

Shifts duration to time_unit. It is similar to convert/1 but this function returns a duration tuple, while convert/1 just returns an integer value.

Warning

This function is lossy because it rounds down to the nearest whole number.

iex> Moar.Duration.shift({121, :second}, :minute)
{2, :minute}
@spec shift_down(t()) :: t()

Shifts duration to the next smaller unit. Raises if it's already at the smallest unit (nanosecond).

iex> Moar.Duration.shift_down({1, :hour})
{60, :minute}

Shifts duration to the next larger unit. Raises if it's already at the largest unit (approx_year). Rounds the result towards zero.

Warning

This function is lossy because it rounds down to the nearest whole number.

iex> Moar.Duration.shift_up({60, :minute})
{1, :hour}

iex> Moar.Duration.shift_up({125, :minute})
{2, :hour}
@spec to_string(t()) :: String.t()

Shortcut to format(duration, :long). See format/4.