Calculate users age


#1

i know it’s probably really simple, how do i work out someone’s age if i
have their d.o.b. stored as a date in my db.

cheers


#2

Depending on your database, how about doing something with the
datediff() or
similar function? eg: for Postgresql:

select (current_timestamp-dob) / 365.25 from users

I’m sure you could


#3

irb(main):001:0> require ‘date’
=> true
irb(main):002:0> bday = Date.new(1985, 7, 24)
=> #<Date: 4892541/2,0,2299161>
irb(main):003:0> age = (Date.today - bday).to_i / 365
=> 20

This is ruby, so there’s probably a better way. There should be some
slick way of doing it like
bday.years.until(Date.today) # This does not work. But I think it’d
be cool if it did :slight_smile:

Pat


#4

Two things to think about:

  1. In Rails, database is for stoiring raw data, not for calculations.
    There is nothing wrong in using it for calculations when you have some
    good reason to do so, but by default you shouldn’t do it.

  2. days/365, as well as days/365.25 are both approximations (the former
    is wrong more often than the latter). Sure, we can do better than that!

So, how about below?

Alex

require ‘test/unit’
class BirthdayTest < Test::Unit::TestCase

def test_simple
assert_equal 100, age(Date.new(1905, 1, 1), Date.new(2005, 1, 1))
end

def test_age_born_this_year_is_zero
assert_equal 0, age(Date.new(2005, 1, 1), Date.new(2005, 12, 31))
assert_equal 1, age(Date.new(2005, 1, 1), Date.new(2006, 1, 1))
end

def test_doesnt_like_negative_age
birthdate = Date.new(2005, 1, 2)
day_before_birthdate = birthdate - 1
assert_raises(RuntimeError) { age(birthdate, day_before_birthdate)
}
end

def test_works_well_when_birthday_already_passed_this_year
assert_equal 9, age(Date.new(1990, 2, 2), Date.new(2000, 2, 1))
assert_equal 10, age(Date.new(1990, 2, 2), Date.new(2000, 2, 2))
end

def test_handles_feb_29
assert_equal 4, age(Date.new(2000, 2, 29), Date.new(2005, 2, 28))
assert_equal 5, age(Date.new(2000, 2, 29), Date.new(2005, 3, 1))
end

def test_handles_many_years
assert_equal 6000, age(Date.new(-3000, 1, 1), Date.new(3000, 12,
31))
end

end

def age(birthdate, now)
raise “Birthdate (#{birthdate.strftime(’%Y-%m-%d’)}) is after now
(#{now.strftime(’%Y-%m-%d’)})” if birthdate > now

had_birthday_passed_this_year = (now.month * 100 + now.day >=
birthdate.month * 100 + birthdate.day)
if had_birthday_passed_this_year
now.year - birthdate.year
else
now.year - birthdate.year - 1
end
end


#5

Weird, I was just writing something like this in the course of
learning ruby. I doubt you’ll learn anything by just posting a random
question and expecting an answer. You should at least give it a stab.

Hopefully, a more experienced ruby programmer will give me advice to
improve my code.

Here’s my attempt:

birthday.rb code

require “time”

class Birthday

#This can be slimmed down considerably
#but I thought it might be useful to keep them all separate

def initialize(birth_month, birth_date, birth_year)
@birth_month = birth_month
@birth_date = birth_date
@birth_year = birth_year
end

#This puts the birthday in a format that can be manipulated
def bday
bday = @birth_month + “/” + @birth_date + “/” + @birth_year
Time.parse(bday)
end

#This will round off the age
def age
age = ((Time.now - self.bday)/60/60/24/365).to_i
end

end

If you load this from irb using irb> load ‘birthday.rb’

you can put in

irb> john = Birthday.new(“1”, “1”, “1984”)
irb> john.age


#6

Am Sonntag, den 22.01.2006, 21:33 -0700 schrieb Alexey V.:

end
end

I made a small enhancement for the Date class from it:

class date
def age(today = self.class.today)
today.year - year - ((today.month * 100 + today.day >= month * 100 +
day) ? 0 : 1)
end
end

If no attribute is passed, it calculates the age for today. You can pass
a Date object to calculate the age on a specific date.


Norman T.

http://blog.inlet-media.de


#7

Paul B. wrote:

When I copy the text and save it into a file called BirthdayTest.rb
and then try to run it, I get these errors. It seems like Date.new
doesn’t like having 3 arguments. I am using Ruby 1.8.2 on Win XP.
Any idea why this is?

Hmm… worksforme ™

C:\eclipse\workspace\i2>ruby -v
ruby 1.8.2 (2004-12-25) [i386-mswin32]

C:\eclipse\workspace\i2>irb
irb(main):001:0> Date.new(2000, 1, 1)
=> #<Date: 4903089/2,0,2299161>

C:>age
Loaded suite C:/age
Started

Finished in 0.031 seconds.

6 tests, 9 assertions, 0 failures, 0 errors

Alex


#8

Weird…

E:\home\pbarry\projects\rails>ruby -v
ruby 1.8.2 (2004-12-25) [i386-mswin32]

E:\home\pbarry\projects\rails>irb
irb(main):001:0> Date.new(2000,1,1)
ArgumentError: wrong number of arguments (3 for 0)
from (irb):1:in initialize' from (irb):1:innew’
from (irb):1


#9

When I copy the text and save it into a file called BirthdayTest.rb and
then
try to run it, I get these errors. It seems like Date.new doesn’t like
having 3 arguments. I am using Ruby 1.8.2 on Win XP. Any idea why this
is?

Loaded suite BirthdayTest
Started
EEEEEE
Finished in 0.0 seconds.

  1. Error:
    test_age_born_this_year_is_zero(BirthdayTest):
    ArgumentError: wrong number of arguments (3 for 0)
    BirthdayTest.rb:9:in initialize' BirthdayTest.rb:9:innew’
    BirthdayTest.rb:9:in `test_age_born_this_year_is_zero’

  2. Error:
    test_doesnt_like_negative_age(BirthdayTest):
    ArgumentError: wrong number of arguments (3 for 0)
    BirthdayTest.rb:14:in initialize' BirthdayTest.rb:14:innew’
    BirthdayTest.rb:14:in `test_doesnt_like_negative_age’

  3. Error:
    test_handles_feb_29(BirthdayTest):
    ArgumentError: wrong number of arguments (3 for 0)
    BirthdayTest.rb:25:in initialize' BirthdayTest.rb:25:innew’
    BirthdayTest.rb:25:in `test_handles_feb_29’

  4. Error:
    test_handles_many_years(BirthdayTest):
    ArgumentError: wrong number of arguments (3 for 0)
    BirthdayTest.rb:30:in initialize' BirthdayTest.rb:30:innew’
    BirthdayTest.rb:30:in `test_handles_many_years’

  5. Error:
    test_simple(BirthdayTest):
    ArgumentError: wrong number of arguments (3 for 0)
    BirthdayTest.rb:5:in initialize' BirthdayTest.rb:5:innew’
    BirthdayTest.rb:5:in `test_simple’

  6. Error:
    test_works_well_when_birthday_already_passed_this_year(BirthdayTest):
    ArgumentError: wrong number of arguments (3 for 0)
    BirthdayTest.rb:20:in initialize' BirthdayTest.rb:20:innew’
    BirthdayTest.rb:20:in
    `test_works_well_when_birthday_already_passed_this_year’

6 tests, 0 assertions, 0 failures, 6 errors


#10

On 1/23/06, SB removed_email_address@domain.invalid wrote:

Weird, I was just writing something like this in the course of
learning ruby. I doubt you’ll learn anything by just posting a random
question and expecting an answer. You should at least give it a stab.

Hopefully, a more experienced ruby programmer will give me advice to
improve my code.

Here’s my attempt:
birthday.rb code
require “time”

I would not recommend using the class time here since it does not
handle dates before 1970-01-01:
irb> Time.parse(“1970-01-01 01:00”)
=> Thu Jan 01 01:00:00 W. Europe Standard Time 1970
irb> Time.parse(“1969-12-31”)
ArgumentError: time out of range
irb> Time.parse(“1970-01-01 00:59”)
ArgumentError: time out of range

Try the Date in date.rb instead.
http://www.ruby-doc.org/core/files/lib/date_rb.html

Something like this looks like it works:
born=Date.strptime(“1969-07-21”)
age=Date.today.year-born.year
age=age-1 if Date.today.yday<born.yday


Jonas
Elfström


#11

Using ‘require “time”’ works fine on my computer (Mac OSX). Jan 1,
1970 is the epoch used in the Time class of ruby and might cause
trouble on other operating systems (according to the pickaxe).

However, I agree that using the Date library will probably make my
code more compact and offer more options.

Thanks for pointing this out.

Sam


#12

Neatest method I found was posted by Justing in the forum, not so long
ago :
http://wrath.rubyonrails.org/pipermail/rails/2005-December/006145.html

def age_at(date, dob)
day_diff = date.day - dob.day
month_diff = date.month - dob.month - (day_diff < 0 ? 1 : 0)
date.year - dob.year - (month_diff < 0 ? 1 : 0)
end


#13

Am Dienstag, den 24.01.2006, 01:17 -0500 schrieb Paul B.:

    from (irb):1

On my Ubuntu i have to require ‘date’, before i can use it.

irb(main):001:0> Date.new(2006,1,1)
NameError: uninitialized constant Date
from (irb):1
irb(main):002:0> require ‘date’
=> true
irb(main):003:0> Date.new(2006,1,1)
=> #<Date: 4907473/2,0,2299161>
irb(main):004:0>

Maybe this helps.


Norman T.

http://blog.inlet-media.de


#14

On 1/24/06, Alan removed_email_address@domain.invalid wrote:

Neatest method I found was posted by Justing in the forum, not so long
ago :
http://wrath.rubyonrails.org/pipermail/rails/2005-December/006145.html

def age_at(date, dob)
day_diff = date.day - dob.day
month_diff = date.month - dob.month - (day_diff < 0 ? 1 : 0)
date.year - dob.year - (month_diff < 0 ? 1 : 0)
end

Sorry, could not help myself:

def age_at(date, dob)
date.year - dob.year - ( (date.yday-dob.yday) < 0 ? 1 : 0 )
end

And now something for the swedish readers. Check out my validation of
swedish “social security number” in Ruby:
http://plea.se/me/validatePnum.html


Jonas
Elfström


#15

So my desktop has the problem as listed in the previous email, but it
seems
to work on my laptop fine:

C:\Documents and Settings\PBarry>ruby -v
ruby 1.8.2 (2004-12-25) [i386-mswin32]

C:\Documents and Settings\PBarry>irb
irb(main):001:0> Date.new(2000,1,1)
=> #<Date: 4903089/2,0,2299161>

But on linux, it doesn’t work:

[root@paulbarry ~]# ruby -v
ruby 1.8.4 (2005-12-24) [i386-linux]
[root@paulbarry ~]# irb
irb(main):001:0> Date.new(2000,1,1)
NameError: uninitialized constant Date
from (irb):1

Any idea what could cause these differences? 3 different environments,
three different results for the same 1 line of code. Kind of makes me
worried that code I write on one machine will not work on another.


#16

On 1/24/06, Paul B. removed_email_address@domain.invalid wrote:

I believe this would techincally not work in leap year, right?

Why not? yday is 1 to 366 in a leap year.
I tested a few dates in and out of leap years and it seems to work
just fine. If you find a case that breaks the functionality please
tell me. Maybe we should take it offlist…


/J

month_diff = date.month - dob.month - (day_diff < 0 ? 1 : 0)
date.year - dob.year - (month_diff < 0 ? 1 : 0)
end

Sorry, could not help myself:

def age_at(date, dob)
date.year - dob.year - ( (date.yday-dob.yday) < 0 ? 1 : 0 )
end


Jonas
Elfström


#17

On 1/24/06, Paul B. removed_email_address@domain.invalid wrote:

But on linux, it doesn’t work:
[root@paulbarry ~]# irb
irb(main):001:0> Date.new(2000,1,1)
NameError: uninitialized constant Date
from (irb):1

Any idea what could cause these differences?

Somehow the date class is preloaded in some of your installations and
in some not, I leave the story behind that to the wizards. Always do:
require ‘date.rb’
and you will be fine in any environment.


Jonas
Elfström


#18

I believe this would techincally not work in leap year, right?


#19

This shows the errors in age_at better:

require ‘test/unit’
class BirthdayTest < Test::Unit::TestCase

def test_age_at_non_leap_year_with_leap_year_dob
assert_equal 19, age_at(Date.new(2007, 3, 1), Date.new(1988, 3, 1))
end

def test_age_at_leap_year_with_non_leap_year_dob
assert_equal 20, age_at(Date.new(2008, 2, 29), Date.new(1987, 3, 1))
end

def test_age_non_leap_year_with_leap_year_dob
assert_equal 19, age(Date.new(2007, 3, 1), Date.new(1988, 3, 1))
end

def test_age_leap_year_with_non_leap_year_dob
assert_equal 20, age(Date.new(2008, 2, 29), Date.new(1987, 3, 1))
end

end

def age_at(date, dob)
date.year - dob.year - ( (date.yday-dob.yday) < 0 ? 1 : 0 )
end

def age(now, birthdate)
had_birthday_passed_this_year = (now.month * 100 + now.day >=
birthdate.month * 100 + birthdate.day)
if had_birthday_passed_this_year
now.year - birthdate.year
else
now.year - birthdate.year - 1
end
end


#20

it doesn’t work because Date.new(2008,3,1).yday is 61, whereas
Date.new(2007,3,1).yday
is 60. Looks like Alekexy’s age method is correct and the age_at method
that relies on yday is incorrect:

require ‘test/unit’
class BirthdayTest < Test::Unit::TestCase

def test_age_at_non_leap_year_with_leap_year_dob
assert_equal 19, age_at(Date.new(2007, 3, 1), Date.new(1988, 3, 1))
end

def test_age_at_leap_year_with_leap_year_dob
assert_equal 20, age_at(Date.new(2008, 3, 1), Date.new(1988, 3, 1))
end

def test_age_at_non_leap_year_with_non_leap_year_dob
assert_equal 20, age_at(Date.new(2007, 3, 1), Date.new(1987, 3, 1))
end

def test_age_at_leap_year_with_non_leap_year_dob
assert_equal 21, age_at(Date.new(2008, 3, 1), Date.new(1987, 3, 1))
end

def test_age_non_leap_year_with_leap_year_dob
assert_equal 19, age(Date.new(2007, 3, 1), Date.new(1988, 3, 1))
end

def test_age_leap_year_with_leap_year_dob
assert_equal 20, age(Date.new(2008, 3, 1), Date.new(1988, 3, 1))
end

def test_age_non_leap_year_with_non_leap_year_dob
assert_equal 20, age(Date.new(2007, 3, 1), Date.new(1987, 3, 1))
end

def test_age_leap_year_with_non_leap_year_dob
assert_equal 21, age(Date.new(2008, 3, 1), Date.new(1987, 3, 1))
end

end

def age_at(date, dob)
date.year - dob.year - ( (date.yday-dob.yday) < 0 ? 1 : 0 )
end

def age(now, birthdate)
had_birthday_passed_this_year = (now.month * 100 + now.day >=
birthdate.month * 100 + birthdate.day)
if had_birthday_passed_this_year
now.year - birthdate.year
else
now.year - birthdate.year - 1
end
end