Current section

Files

Jump to
every lib every.ex
Raw

lib/every.ex

defmodule Every do
@moduledoc """
Every gives you ability to use `Process.send_after/3` with calculated
intervals which can be rounded to every:
1. Minute,
2. N minutes,
3. Hour,
4. N Hours.
Every function accepts an optional `relative_to` parameter, which can be used
to fake the current moment in time. If it is not provided, the current time
will be used.
**Note:** All functions return the difference in milliseconds!
"""
require Logger
@doc """
Calculates how many milliseconds left until the next minute starts.
## Examples
iex> now = Timex.parse!("2018-10-14T16:48:12.000Z", "{ISO:Extended}")
iex> Every.minute(now)
48_000
"""
def minute(relative_to \\ Timex.now()) do
(60 - relative_to.second) * 1000
end
@doc """
Calculates how many milliseconds left until the next interval (minutes) will be
reached.
## Examples
iex> now = Timex.parse!("2018-10-14T16:48:12.000Z", "{ISO:Extended}")
iex> Every.minutes(5, now) # 16:50 > 15:50:00 - 16:48:12
108_000
"""
def minutes(interval, relative_to \\ Timex.now())
def minutes(interval, nil) do
Logger.warn(
"Every.minutes(interval, nil) is deprecated! Use Every.minutes(interval) instead."
)
minutes(interval)
end
def minutes(interval, relative_to) do
minutes_until_next_interval = next_interval(relative_to.minute, interval)
{microseconds, _precision} = relative_to.microsecond
relative_to
|> Timex.shift(seconds: -relative_to.second)
|> Timex.shift(microseconds: -microseconds)
|> Timex.shift(minutes: minutes_until_next_interval)
|> Timex.diff(relative_to, :milliseconds)
end
@doc """
Calculates how many seconds left until the next hour starts.
## Examples
iex> now = Timex.parse!("2018-10-14T16:48:12.000Z", "{ISO:Extended}")
iex> Every.hour(now)
708_000
"""
def hour(relative_to \\ Timex.now()) do
{microseconds, _precision} = relative_to.microsecond
relative_to
|> Timex.shift(seconds: -relative_to.second)
|> Timex.shift(microseconds: -microseconds)
|> Timex.shift(minutes: -relative_to.minute)
|> Timex.shift(hours: 1)
|> Timex.diff(relative_to, :milliseconds)
end
@doc """
Calculates how many milliseconds left until the next interval (hours) will be
reached.
## Examples
iex> now = Timex.parse!("2018-10-14T16:48:12.000Z", "{ISO:Extended}")
iex> Every.hours(2, now)
4_308_000
"""
def hours(interval, relative_to \\ Timex.now())
def hours(interval, nil) do
Logger.warn("Every.hours(interval, nil) is deprecated! Use Every.hours(interval) instead.")
hours(interval)
end
def hours(interval, relative_to) do
hours_until_next_interval = next_interval(relative_to.hour, interval)
{microseconds, _precision} = relative_to.microsecond
relative_to
|> Timex.shift(seconds: -relative_to.second)
|> Timex.shift(microseconds: -microseconds)
|> Timex.shift(minutes: -relative_to.minute)
|> Timex.shift(hours: hours_until_next_interval)
|> Timex.diff(relative_to, :milliseconds)
end
@doc """
Calculates how many milliseconds left until the next day starts.
## Examples
iex> now = Timex.parse!("2018-10-14T16:48:12.000Z", "{ISO:Extended}")
iex> Every.day(now) # Time remaining 7h 25m 48s
25_908_000
"""
def day(relative_to \\ Timex.now()) do
relative_to
|> Timex.shift(days: 1)
|> Timex.beginning_of_day()
|> Timex.diff(relative_to, :milliseconds)
end
defp next_interval(value, round_value) do
# Uses `rem` function to get remainder for value
# then calculates next step value, for example
# value=48, round_value=15
# then the result will look like
# 15 - (48%15) = 60
round_value - rem(value, round_value)
end
end