Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby age method using date of birth including months

I have a method on my user model to calculate the user's age and return a human readable string. My user's can be between 1 month old and above so the returned string is different depending on if the person is "2 months old" or "1 year old" or "2 years and 3 months old".

I have reviewed few SO posts to come to this solution. Is there anything I am missing? Leap years? Thank you!

def age
    dob = self.date_of_birth

    # if a date of birth is not nil
    if dob != nil 

      # get current date  
      now = Date.current

      # has person had their birthday yet this year
      had_birthday = ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? true : false) 

      # if yes then subtract this year from birthday year, if not then also subtract 1 to get how many full years old they are
      years = now.year - dob.year - (had_birthday ? 0 : 1)

      # get the calendar month difference from birthdya calendar month and today's calendar month.  if they have not had their birthdya yet then subtract the difference from 12
      months = had_birthday ? now.month - dob.month : 12 - (now.month - dob.month)

      # for under 1 year olds
      if years == 0
        return months > 1 ? months.to_s + " months old" : months.to_s + " month old"  

      # for 1 year olds
      elsif years == 1
        return months > 1 ? years.to_s + " year and " + months.to_s + " months old" : years.to_s + " year and " + months.to_s + " month old" 

      # for older than 1
      else
        return months > 1 ? years.to_s + " years and " + months.to_s + " months old" : years.to_s + " years and " + months.to_s + " month old"
      end

    # No date of birth saved so can not calculate age
    else
      return "No Date of Birth"
    end
  end
like image 895
MicFin Avatar asked Dec 15 '22 08:12

MicFin


1 Answers

While this may be better posted to a codereview site, I will still give you my thoughts.

You have written a fairly long method for what could be a couple smaller ones.

First, I would write one method that takes the number of years an months and separate that into its own function.

def readable_age(years, months)
  # for under 1 year olds
  if years == 0
    return months > 1 ? months.to_s + " months old" : months.to_s + " month old"  

  # for 1 year olds
  elsif years == 1
    return months > 1 ? years.to_s + " year and " + months.to_s + " months old" : years.to_s + " year and " + months.to_s + " month old" 

  # for older than 1
  else
    return months > 1 ? years.to_s + " years and " + months.to_s + " months old" : years.to_s + " years and " + months.to_s + " month old"
  end
end

Though, if you do not mind adding some dependencies to your project, you can take advantage of the actionview gem, you can take advantage of the pluralize function. Something along the lines of

def readable_age(years, months)
  year_text = ''
  if years != 0
    year_text = "#{years} #{pluralize('year', years)} and "
  end

  "#{year_text}#{pluralize('month', months)} old"
end

Now for your function to calculate the number of years and months.

def age(t)
  dob = self.date_of_birth

  months = (t.year * 12 + t.month) - (dob.year * 12 + dob.month)

  # months / 12 will give the number of years
  # months % 12 will give the number of months
  readable_age(months / 12, months % 12)
end

EDIT

The reason I am passing a date object into the age function is to allow you to calculate the the age of a person for a given time stamp. It also makes it easier to test a function if it produces the same result given the same inputs.

like image 160
Justin Wood Avatar answered Jan 09 '23 13:01

Justin Wood