Packages

Verifies if a person born at a given date meets provided age requirements.

Current section

Files

Jump to
age_guard lib age_guard.ex
Raw

lib/age_guard.ex

defmodule AgeGuard do
@moduledoc """
Verifies if a person born at a given date meets provided age requirements.
It checks given DOB (date of birth) against given age.
Useful when registering users.
Acceptable formats of DOB (mix of integers and strings):
```
1, 12, 2020
01, 03, 2010
"01", "12", "2020"
"1", "3", "2010"
"03", "March", "2011"
"17", "Mar", "2018"
17, "Mar", 2019
"13", 02, "2019"
```
Also does some dates validations (dates from the future are rejected).
## Installation
The package can be installed by adding `age_guard` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:age_guard, "~> 0.1.0"}
]
end
```
and running `mix deps.get` in your console to fetch from Hex.
"""
@doc """
Checks if enough years (min_age) have passed since given date.
```
AgeGuard.is_old_enough?(day_of_birth, month_of_birth, year_of_birth, required_age)
```
### Examples
```
iex> AgeGuard.is_old_enough?("1","5","2019", 21)
false
iex> AgeGuard.is_old_enough?(3, "March", 2000, 21)
false
iex> AgeGuard.is_old_enough?(3, 3, 2000, 18)
true
iex> AgeGuard.is_old_enough?(3, "Dec", 1995, 18)
true
```
"""
def is_old_enough?(day, month, year, min_age \\ 18) do
if is_integer(min_age) && min_age > 0 do
with {:ok, dob} <- verify_date(day, month, year) do
meets_req_age?(dob, min_age)
end
else
{:error, "min_age must be a positive integer"}
end
end
# makes the actual calculations
defp meets_req_age?(dob, min_req_age) do
today = Date.utc_today()
iso_day = format_to_iso(today.day)
iso_month = format_to_iso(today.month)
case Date.from_iso8601("#{to_string(today.year - min_req_age)}-#{iso_month}-#{iso_day}") do
{:ok, today_minus_min_req_age} ->
days_between_today_and_dob = Enum.count(Date.range(today, dob))
days_between_today_and_req_yrs = Enum.count(Date.range(today, today_minus_min_req_age))
days_between_today_and_dob >= days_between_today_and_req_yrs
_ ->
false
end
end
# verifies if day, date, month make sense
defp verify_date(day, month, year) do
# ensure date meets acceptable ISO format
iso_day = format_to_iso(day)
iso_month = format_to_iso(month)
iso_year = to_string(year)
today = Date.utc_today()
case Date.from_iso8601("#{iso_year}-#{iso_month}-#{iso_day}") do
{:ok, iso_dob} ->
if Date.diff(today, iso_dob) < 0 do
{:error, "DOB cannot be in the future"}
else
{:ok, iso_dob}
end
_ ->
{:error, "invalid format"}
end
end
# ISO format requires day and month to be two digits
defp format_to_iso(val) do
cond do
is_integer(val) and val < 10 ->
"0" <> to_string(val)
is_binary(val) and String.length(val) > 2 ->
month_to_iso(String.downcase(val))
is_binary(val) and String.length(val) == 1 ->
"0" <> val
true ->
to_string(val)
end
end
defp month_to_iso(month) when month in ["january", "jan"], do: "01"
defp month_to_iso(month) when month in ["february", "feb"], do: "02"
defp month_to_iso(month) when month in ["march", "mar"], do: "03"
defp month_to_iso(month) when month in ["april", "apr"], do: "04"
defp month_to_iso(month) when month in ["may", "may"], do: "05"
defp month_to_iso(month) when month in ["june", "jun"], do: "06"
defp month_to_iso(month) when month in ["july", "jul"], do: "07"
defp month_to_iso(month) when month in ["august", "aug"], do: "08"
defp month_to_iso(month) when month in ["september", "sept", "sep"], do: "09"
defp month_to_iso(month) when month in ["october", "oct"], do: "10"
defp month_to_iso(month) when month in ["november", "nov"], do: "11"
defp month_to_iso(month) when month in ["december", "dec"], do: "12"
defp month_to_iso(_), do: "incorrect"
end