Duration utilities for time span calculations in Predicator expressions.
This module provides functions to create, manipulate, and convert duration values for use in relative date expressions and date arithmetic.
Examples
iex> Predicator.Duration.new(days: 3, hours: 8)
%{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0, milliseconds: 0}
iex> Predicator.Duration.from_units([{"3", "d"}, {"8", "h"}])
{:ok, %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0, milliseconds: 0}}
iex> Predicator.Duration.to_seconds(%{days: 1, hours: 2, minutes: 30})
95400
Summary
Functions
Adds a duration to a Date, returning a Date.
Adds a duration to a DateTime, returning a DateTime.
Adds a specific unit amount to a duration.
Creates a duration from parsed unit pairs.
Creates a new duration with specified time units.
Parses a duration literal string into a duration map.
Subtracts a duration from a Date, returning a Date.
Subtracts a duration from a DateTime, returning a DateTime.
Converts a duration to total milliseconds (approximate for months and years).
Converts a duration to total seconds (approximate for months and years).
Converts a duration to a human-readable string.
Functions
@spec add_to_date(Date.t(), Predicator.Types.duration()) :: Date.t()
Adds a duration to a Date, returning a Date.
Examples
iex> date = ~D[2024-01-15]
iex> duration = Predicator.Duration.new(days: 3, weeks: 1)
iex> Predicator.Duration.add_to_date(date, duration)
~D[2024-01-25]
@spec add_to_datetime(DateTime.t(), Predicator.Types.duration()) :: DateTime.t()
Adds a duration to a DateTime, returning a DateTime.
Examples
iex> datetime = ~U[2024-01-15T10:30:00Z]
iex> duration = Predicator.Duration.new(days: 2, hours: 3, minutes: 30)
iex> Predicator.Duration.add_to_datetime(datetime, duration)
~U[2024-01-17T14:00:00Z]
@spec add_unit(Predicator.Types.duration(), binary(), non_neg_integer()) :: Predicator.Types.duration()
Adds a specific unit amount to a duration.
Examples
iex> duration = Predicator.Duration.new(days: 1)
iex> Predicator.Duration.add_unit(duration, "h", 3)
%{years: 0, months: 0, weeks: 0, days: 1, hours: 3, minutes: 0, seconds: 0, milliseconds: 0}
@spec from_units([{binary(), binary()}]) :: {:ok, Predicator.Types.duration()} | {:error, binary()}
Creates a duration from parsed unit pairs.
Takes a list of {value, unit} tuples and converts them to a duration.
Examples
iex> Predicator.Duration.from_units([{"3", "d"}, {"8", "h"}])
{:ok, %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 0, seconds: 0, milliseconds: 0}}
iex> Predicator.Duration.from_units([{"invalid", "d"}])
{:error, "Invalid duration value: invalid"}
@spec new(keyword()) :: Predicator.Types.duration()
Creates a new duration with specified time units.
All unspecified units default to 0.
Examples
iex> Predicator.Duration.new(days: 2, hours: 3)
%{years: 0, months: 0, weeks: 0, days: 2, hours: 3, minutes: 0, seconds: 0, milliseconds: 0}
iex> Predicator.Duration.new()
%{years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0, milliseconds: 0}
@spec parse(binary()) :: {:ok, Predicator.Types.duration()} | :error
Parses a duration literal string into a duration map.
This is the inverse of to_string/1: the whole string must be a sequence
of one or more <digits><unit> pairs, with no whitespace, no sign, and no
partial consumption. The accepted units are exactly the eight to_string/1
emits - y, mo, w, d, h, m, s, ms - with mo and ms matched
before the single-character units so "1mo" is one month, not one minute
followed by a stray o. Values are non-negative integers. Repeated units
accumulate, matching add_unit/3. Anything else - a bad unit, trailing or
leading junk, a sign, a bare number, or the empty string - is :error.
Examples
iex> Predicator.Duration.parse("3d8h30m")
{:ok, %{years: 0, months: 0, weeks: 0, days: 3, hours: 8, minutes: 30, seconds: 0, milliseconds: 0}}
iex> Predicator.Duration.parse("0s")
{:ok, %{years: 0, months: 0, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0, milliseconds: 0}}
iex> Predicator.Duration.parse("1d ")
:error
@spec subtract_from_date(Date.t(), Predicator.Types.duration()) :: Date.t()
Subtracts a duration from a Date, returning a Date.
Examples
iex> date = ~D[2024-01-25]
iex> duration = Predicator.Duration.new(days: 3, weeks: 1)
iex> Predicator.Duration.subtract_from_date(date, duration)
~D[2024-01-15]
@spec subtract_from_datetime(DateTime.t(), Predicator.Types.duration()) :: DateTime.t()
Subtracts a duration from a DateTime, returning a DateTime.
Examples
iex> datetime = ~U[2024-01-17T14:00:00Z]
iex> duration = Predicator.Duration.new(days: 2, hours: 3, minutes: 30)
iex> Predicator.Duration.subtract_from_datetime(datetime, duration)
~U[2024-01-15T10:30:00Z]
@spec to_milliseconds(Predicator.Types.duration()) :: integer()
Converts a duration to total milliseconds (approximate for months and years).
Uses approximate conversions:
- 1 month = 30 days
- 1 year = 365 days
Examples
iex> Predicator.Duration.to_milliseconds(%{seconds: 1, milliseconds: 500})
1500
iex> Predicator.Duration.to_milliseconds(%{minutes: 1, seconds: 30, milliseconds: 250})
90250
@spec to_seconds(Predicator.Types.duration()) :: integer()
Converts a duration to total seconds (approximate for months and years).
Uses approximate conversions:
- 1 month = 30 days
- 1 year = 365 days
Examples
iex> Predicator.Duration.to_seconds(%{days: 1, hours: 2, minutes: 30, seconds: 15})
95415
iex> Predicator.Duration.to_seconds(%{weeks: 2})
1209600
@spec to_string(Predicator.Types.duration()) :: binary()
Converts a duration to a human-readable string.
Examples
iex> duration = Predicator.Duration.new(days: 3, hours: 8, minutes: 30)
iex> Predicator.Duration.to_string(duration)
"3d8h30m"
iex> duration = Predicator.Duration.new(weeks: 2)
iex> Predicator.Duration.to_string(duration)
"2w"