3
votes

Let's say I have a Person model and each person has a birthday field set as an Ecto.Date column.

So if I retrieve a record from the database which represents an instance of this model, what is the best way to get this person's age from his birthday column?

3

3 Answers

3
votes

Not sure about 'best way', but this is a way:

defmodule AgeCalc do
  @spec age(Ecto.Date.t, atom|{integer, integer, integer}) :: integer
  def age(%Ecto.Date{day: d, month: m, year: y}, as_of \\ :now) do
    do_age({y, m, d}, as_of)
  end

  ###########
  # Internals
  ###########
  @doc false
  def do_age(birthday, :now) do
    {today, _time} = :calendar.now_to_datetime(:erlang.now)
    calc_diff(birthday, today)
  end
  def do_age(birthday, date), do: calc_diff(birthday, date)

  @doc false
  def calc_diff({y1, m1, d1}, {y2, m2, d2}) when m2 > m1 or (m2 == m1 and d2 >= d1) do
    y2 - y1
  end
  def calc_diff({y1,_,_}, {y2,_,_}), do: (y2 - y1) - 1
end

Usage goes like this: (today is September 27th, 2015)

iex(1)> AgeCalc.age(%Ecto.Date{day: 27, month: 9, year: 2000})
15
iex(2)> AgeCalc.age(%Ecto.Date{day: 28, month: 9, year: 2000})
14

or, for a manual calculation against something other than today:

iex(3)> AgeCalc.age(%Ecto.Date{day: 28, month: 9, year: 2000}, {2034, 12, 25})
34
1
votes

I use fragment from Ecto's Query API. I have a user table with a birthday column and am using the following query:

Repo.all(from u in User, select: fragment("age(?)", u.birthday))

The query returns a %Postgrex.Interval{} with fields days, months, and secs. Take months and div by 12 to get the age.

I love Timex but using Postgres' native age function seems cleaner and quicker to me.

1
votes

I did it in elixir like this

  def calculate_age(dob) when is_binary(dob)
  def calculate_age(dob) when is_binary(dob) do
    today = Date.utc_today()
    dob = Date.from_iso8601! dob
    today.year - dob.year
  end