views:

1668

answers:

6

I'd to get a person's age from it's birthday. now - birthday / 365 doesnt work, because some year has 366 days. I came up with following code:

now = Date.today
year = now.year - birth_date.year

if (date+year.year) > now
  year = year - 1
end

Is there a more rubyish way to calculate age?

+5  A: 

Use this:

def age
  now = Time.now.utc.to_date
  now.year - birthday.year - (birthday.to_date.change(:year => now.year) > now ? 1 : 0)
end
Adinochestva
This breaks if birthday.to_date is a leap year and the current year isn't. Not a big occurrence, but it's been causing me problems.
Phil
+5  A: 

I've found this solution to work well and be readable for other people:

    age = Date.today.year - birthday.year
    age -= 1 if Date.today < birthday + age.years #for days before birthday

Easy and you don't need to worry about handling leap year and such.

PJ
Chuck
You're right -- sorry I assumed Rails as the question was tagged with it. But yes, easily modified for Ruby only.
PJ
well thought out, easy to read and does the job... way better than the rest of the answers in this thread
Faisal
+1  A: 

The answers so far are kinda weird. Your original attempt was pretty close to the right way to do this:

birthday = DateTime.new(1900, 1, 1)
age = (DateTime.now - birthday) / 365.25 # or (1.year / 1.day)

You will get a fractional result, so feel free to convert the result to an integer with to_i. This is a better solution because it correctly treats the date difference as a time period measured in days (or seconds in the case of the related Time class) since the event. Then a simple division by the number of days in a year gives you the age. When calculating age in years this way, as long as you retain the original DOB value, no allowance needs to be made for leap years.

Bob Aman
birthday = Time.mktime(1960,5,5) gives me out of range (epoch problems?)
Andrew Grimm
Yeah, go go epoch issues. I've updated the answer to resolve this.
Bob Aman
`birthday = DateTime.now - 1.year` gives me an age of 0. Unfortunately, dividing by 365.25 is a little imprecise.
Samir Talwar
You can't subtract 1.year like that from a DateTime object. 1.year resolves to the number of seconds in a year. DateTime objects operate based on days. For example: (DateTime.now - 365.25).strftime("%D") As for precision, if you're really just dealing with birthdays, it's plenty precise. Fact of the matter is, people are already quite imprecise when it comes to ages. We're born at a precise moment in time, but we don't usually give the exact hour, minute, and second of our birth when we write down our DOB. My argument here is that you really don't want to do this calculation manually.
Bob Aman
This doesn't work for people born before 1900. For example Gertrude Baines is reported as having an age of 114.9979 on her birthday in 2009.
Andrew Grimm
Then you've done your math wrong.(DateTime.now - DateTime.new(1800, 1, 1)).to_f / 365.25 #=> 209.357540666406
Bob Aman
Oh, my bad, I missed what you were getting at. Didn't read all the way through, thought you meant a problem with out of range dates. Yes, that does happen. But it also happens for any algorithm involving time subtraction. You can compensate by rounding at certain thresholds, however, I'm inclined to say that you're working too hard at that point.
Bob Aman
Time seems to be reimplemented for ruby 1.9.2, so hopefully it won't have epoch issues.
Andrew Grimm
Oh, nice. I certainly hope so.
Bob Aman
+1  A: 

I'm a ruby newbie. I tried to answer the question before checking out the other replies. It seems to work but I'd appreciate it if anyone check or point out what could be problematic with my answer..

age = now.year - bday.year
age -= 1 if now.to_a[7] < bday.to_a[7]
testr
+7  A: 

I know I'm late to the party here, but the accepted answer will break horribly when trying to work out the age of someone born on the 29th February on a leap year. This is because the call to birthday.to_date.change(:year => now.year) creates an invalid date.

I used the following code (in a Rails project) instead:

def age(dob)
  now = Time.now.utc.to_date
  now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
Phil
A: 
  def birthday(user)
    today = Date.today
    new = user.birthday.to_date.change(:year => today.year)
    user = user.birthday
    if Date.civil_to_jd(today.year, today.month, today.day) >= Date.civil_to_jd(new.year, new.month, new.day)
      age = today.year - user.year
    else
      age = (today.year - user.year) -1
    end
    age
  end
Brian