Current section
Files
Jump to
Current section
Files
lib/add.ex
defmodule Momento.Add do
import Momento.Guards
import Momento.Helpers
@moduledoc """
This module holds all the various `add/3` methods.
"""
@doc """
Add a specified amount of time. Available atoms are `:years`, `:months`, `:days`, `:hours`, `:minutes`, `:seconds`,
`:milliseconds` and `:microseconds`.
## Examples
iex> Momento.date! |> Momento.add(5, :years)
%DateTime{calendar: Calendar.ISO, day: 1, hour: 22, microsecond: {703979, 6},
minute: 34, month: 7, second: 50, std_offset: 0, time_zone: "Etc/UTC",
utc_offset: 0, year: 2021, zone_abbr: "UTC"}
...> Momento.date! |> Momento.add(5, :days)
%DateTime{calendar: Calendar.ISO, day: 6, hour: 22, microsecond: {904112, 6},
minute: 37, month: 7, second: 56, std_offset: 0, time_zone: "Etc/UTC",
utc_offset: 0, year: 2016, zone_abbr: "UTC"}
...> Momento.date! |> Momento.add(5, :hours) |> Momento.add(15, :minutes)
%DateTime{calendar: Calendar.ISO, day: 2, hour: 3, microsecond: {546990, 6},
minute: 59, month: 7, second: 26, std_offset: 0, time_zone: "Etc/UTC",
utc_offset: 0, year: 2016, zone_abbr: "UTC"}
"""
@spec add(DateTime.t, integer, atom) :: DateTime.t
# Singular to plural
def add(datetime, num, :year), do: add(datetime, num, :years)
def add(datetime, num, :month), do: add(datetime, num, :months)
def add(datetime, num, :day), do: add(datetime, num, :days)
def add(datetime, num, :hour), do: add(datetime, num, :hours)
def add(datetime, num, :minute), do: add(datetime, num, :minutes)
def add(datetime, num, :second), do: add(datetime, num, :seconds)
def add(datetime, num, :millisecond), do: add(datetime, num, :milliseconds)
def add(datetime, num, :microsecond), do: add(datetime, num, :microseconds)
# Years
# Base case
def add(%DateTime{year: year} = datetime, num, :years)
when natural?(num),
do: %DateTime{datetime | year: year + num}
# Months
# Base case
def add(%DateTime{month: month} = datetime, num, :months)
when natural?(num) and month + num <= 12,
do: %DateTime{datetime | month: month + num}
# Many years worth of months
def add(%DateTime{} = datetime, num, :months)
when positive?(num) and num > 11
do
years = floor(num / 12)
add(datetime, years, :years) |> add(num - years * 12, :months)
end
# Rollover months to the next year
def add(%DateTime{month: month} = datetime, num, :months)
when positive?(num) and month + num > 12,
do: add(%DateTime{datetime | month: 1}, 1, :years) |> add(num - month - 1, :months)
# Days
# Base case
def add(%DateTime{month: month, day: day} = datetime, num, :days)
when natural?(num) and day + num <= days_in_month(month),
do: %DateTime{datetime | day: day + num}
# Many months worth of days
def add(%DateTime{month: month} = datetime, num, :days)
when positive?(num) and num > days_in_month(month),
do: add(datetime, 1, :months) |> add(num - days_in_month(month), :days)
# Rollver days to be the next month
def add(%DateTime{month: month, day: day} = datetime, num, :days)
when positive?(num) and day + num > days_in_month(month),
do: add(%DateTime{datetime | day: 1}, 1, :months) |> add(num - (days_in_month(month) - day) - 1, :days)
# Hours
# Base case
def add(%DateTime{hour: hour} = datetime, num, :hours)
when natural?(num) and num + hour < 24,
do: %DateTime{datetime | hour: num + hour}
# Many days worth of hours
def add(%DateTime{} = datetime, num, :hours)
when positive?(num) and num > 24
do
days = floor(num / 24)
add(datetime, days, :days) |> add(num - days * 24, :hours)
end
# Rollover hours to be the next day
def add(%DateTime{hour: hour} = datetime, num, :hours)
when positive?(num) and num + hour > 23,
do: add(%DateTime{datetime | hour: 0}, 1, :days) |> add(num - (24 - hour), :hours)
# Minutes
# Base case
def add(%DateTime{minute: minute} = datetime, num, :minutes)
when natural?(num) and num + minute < 60,
do: %DateTime{datetime | minute: num + minute}
# Many hours worth o fminutes
def add(%DateTime{} = datetime, num, :minutes)
when positive?(num) and num > 60
do
hours = floor(num / 60)
add(datetime, hours, :hours) |> add(num - hours * 60, :minutes)
end
# Rollover minutes to be the next hour
def add(%DateTime{minute: minute} = datetime, num, :minutes)
when positive?(num) and num + minute >= 60,
do: add(%DateTime{datetime | minute: 0}, 1, :hours) |> add(num - (60 - minute) - 1, :minutes)
# Seconds
# Base case
def add(%DateTime{second: second} = datetime, num, :seconds)
when natural?(num) and num + second < 60,
do: %DateTime{datetime | second: num + second}
# Many minutes worth of seconds
def add(%DateTime{} = datetime, num, :seconds)
when positive?(num) and num > 60
do
minutes = floor(num / 60)
add(datetime, minutes, :minutes) |> add(num - minutes * 60, :seconds)
end
# Rollover seconds to be the next minute
def add(%DateTime{second: second} = datetime, num, :seconds)
when positive?(num) and num + second >= 60,
do: add(%DateTime{datetime | second: 0}, 1, :minutes) |> add(num - (60 - second) - 1, :seconds)
# Milliseconds
# TODO: This doesn't seem right and is incomplete
# Base case
def add(%DateTime{microsecond: {microsecond, precision}} = datetime, num, :milliseconds)
when natural?(num) and num <= 999,
do: %DateTime{datetime | microsecond: {microsecond + num * millisecond_factor(precision), precision}}
# Many seconds worth of milliseconds
def add(%DateTime{microsecond: {_, precision}} = datetime, num, :milliseconds)
when positive?(num) and num > 999 and precision >= 3
do
seconds = Float.floor(num / millisecond_factor(precision)) |> round
add(datetime, seconds, :seconds) |> add(num - seconds * millisecond_factor(precision), :milliseconds)
end
# Microseconds
# Base case
def add(%DateTime{microsecond: {microsecond, precision}} = datetime, num, :microseconds)
when natural?(num) and precision === 6 and microsecond + num <= 999999,
do: %DateTime{datetime | microsecond: {microsecond + num, precision}}
# Many seconds worth of microseconds
def add(%DateTime{microsecond: {_, precision}} = datetime, num, :microseconds)
when positive?(num) and precision === 6 and num > 999999
do
seconds = Float.floor(num / microsecond_factor(precision)) |> round
add(datetime, seconds, :seconds) |> add(num - seconds * microsecond_factor(precision), :microseconds)
end
end