Calculating the age given DOB


#1

Hello, fellow rubyists.

I have a problem. I’m trying to write a simple program which calculates
the age of a person given the person’s date of birth in an instance of a
Date class. I have no idea what is the best way to do this. I don’t feel
like using any ugly “hacks” to do it, and I wonder if there is any good
(perhaps mathematical) algorithm out there to solve the problem.

–Deniz D.


#2

Here is a free Ruby Favicon for all who wish to use it.
Just place it in the top directory of a site (or the appropriate
place for a web app) and do the appropriate html.
John J.


#3

Deniz D. wrote:

Hello, fellow rubyists.

I have a problem. I’m trying to write a simple program which calculates
the age of a person given the person’s date of birth in an instance of a
Date class. I have no idea what is the best way to do this. I don’t feel
like using any ugly “hacks” to do it, and I wonder if there is any good
(perhaps mathematical) algorithm out there to solve the problem.

–Deniz D.

(DateTime.now - birthdate) / 365.25


#4

Brian A. wrote:

–Deniz D.

(DateTime.now - birthdate) / 365.25

And this would work with Date objects?


#5

On Wed, 2007-03-14 at 23:55 +0900, Deniz D. wrote:

Hello, fellow rubyists.

I have a problem. I’m trying to write a simple program which calculates
the age of a person given the person’s date of birth in an instance of a
Date class. I have no idea what is the best way to do this. I don’t feel
like using any ugly “hacks” to do it, and I wonder if there is any good
(perhaps mathematical) algorithm out there to solve the problem.

–Deniz D.

rthompso@jhereg:~$ irb
irb(main):001:0> require ‘date’
=> true
irb(main):002:0> bd = Date.new(1966, 1, 21)
=> #<Date: 4878293/2,0,2299161>
irb(main):003:0> today = Date.new(2007,3,14)
=> #<Date: 4908347/2,0,2299161>
irb(main):004:0> age = today.year - bd.year
=> 41


#6

Brian A. wrote:

(DateTime.now - birthdate) / 365.25

That’s not very reliable. There are algorithms that calculate
the number of days since some epoch from a date, and the
reverse, based on Fortran code that was posted in the CACM
sometime around 1970. I have a copy somewhere, but there
must be a modern version already in Ruby. If you can’t
find a way, I’ll dig out mine.

Just convert both dates to days-since-epoch and subtract.

Clifford.


#7

Deniz D. wrote:

–Deniz D.

(DateTime.now - birthdate) / 365.25

And this would work with Date objects?

Why don’t you give it a try?


#8

Reid T. wrote:

Consider when today.month < bd.month - you’ll have an off-by-one error.


#9

Brian A. wrote:

Clifford H. wrote:

Brian A. wrote:

(DateTime.now - birthdate) / 365.25
Do you mind if I ask why you make the statement, “That’s not very
reliable.” ?

I have written genealogy software, and sometimes I want to know
how old someone was on a given date, like when they married.
1900 was not a leap year, as I’m sure you know. Neither was
1800 or 1700, but 1600 was. Does that make more sense now?

If I write a “library function” like this, I like to be a stickler,
because it’s usually myself that gets tripped up.


#10

Clifford H. wrote:

Just convert both dates to days-since-epoch and subtract.

Clifford.

Do you mind if I ask why you make the statement, “That’s not very
reliable.” ? For what values of birthdate (an instance of Date) would
the above expression not be “reliable” ?


#11

Brian A. schrieb:

Consider when today.month < bd.month - you’ll have an off-by-one error.
age -= 1 if today.month < bd.month || today.month == bd.month &&
today.day < bd.day

I don’t know how people born at February 29th celebrate their birthday
in non-leap years. The code above makes them one year older on March
1st.

Regards,
Pit


#12

Clifford H. wrote:

1800 or 1700, but 1600 was. Does that make more sense now?

If I write a “library function” like this, I like to be a stickler,
because it’s usually myself that gets tripped up.

Gotcha. I saw “simple program” and was thinking more in terms of
approximate real values vs. discrete values (e.g. gaining a year of age
in an instant) which is why I didn’t throw in a round. My guess is that
the OP probably wants the discrete value though, so the other part of
the thread should do the trick.


#13

Le 14 mars à 15:47, Deniz D. a écrit :

Hello, fellow rubyists.

I have a problem. I’m trying to write a simple program which calculates
the age of a person given the person’s date of birth in an instance of a
Date class. I have no idea what is the best way to do this. I don’t feel
like using any ugly “hacks” to do it, and I wonder if there is any good
(perhaps mathematical) algorithm out there to solve the problem.

Depends on what you want exactly. If you want to compute the “usual”
age (i.e. I’ll say I’m 31 years old, not 31.7426420260096), you might
try this :

d1 = Date.today
=> #<Date: 4908347/2,0,2299161>

d2 = Date.new(1975,6,16)
=> #<Date: 4885159/2,0,2299161>

(d1.year - d2.year) - (d1.yday >= d2.yday ? 0 : 1)
=> 31

d1 = Date.new(2007, 06, 16)
=> #<Date: 4908535/2,0,2299161>

(d1.year - d2.year) - (d1.yday >= d2.yday ? 0 : 1)
=> 32

Fred


#14

Thats gr8 !
thanks.


#15

I think we can honestly assume that if you are born on leap day, you
become highly tolerant of systems that don’t even accept leap day
birthdays and probably write Feb 28 or Mar 1 consistently by habit.


#16

F. Senault schrieb:

(d1.year - d2.year) - (d1.yday >= d2.yday ? 0 : 1)

Pardon Fred, using #yday would be short and nice, but this doesn’t work
reliably if one year is a leap year and the other is not. Try it with

d1 = Date.new(2007, 3, 1); d2 = Date.new(2004, 3, 1)
d1 = Date.new(2004, 3, 1); d2 = Date.new(2001, 3, 2)

Regards,
Pit


#17

Le 14 mars 2007 à 20:50, Pit C. a écrit :

F. Senault schrieb:

(d1.year - d2.year) - (d1.yday >= d2.yday ? 0 : 1)

Pardon Fred, using #yday would be short and nice, but this doesn’t work
reliably if one year is a leap year and the other is not.

Yup, you’re perfectly right. The following version seems to work, but
we’re steadily losing elegance… :slight_smile:

d1 = Date.new(2007, 3, 1); d2 = Date.new(2004, 3, 1)
=> #<Date: 4906131/2,0,2299161>

(d1.year - d2.year) - (d1.yday >= Date.new(d1.year, d2.month, 1).yday + d2.day - 1 ? 0 : 1)
=> 3

d1 = d1 - 1
=> #<Date: 4908319/2,0,2299161>

(d1.year - d2.year) - (d1.yday >= Date.new(d1.year, d2.month, 1).yday + d2.day - 1 ? 0 : 1)
=> 2

A pity Date.new doesn’t handle overflows. I’d like to be able to write
this :

Date.new(2007, 2, 29).to_s
=> ‘2007-03-01’

Date.new(2007, 3, 0).to_s
=> ‘2007-02-28’

Fred


#18

Hi! I had this idea, hope it helps:

class Date
def elapsedYearsAndDays(rangeDate)
if self > rangeDate
startDate = rangeDate.clone #clone the values so they are not
changed unintentionally
endDate = self.clone
else
startDate = self.clone
endDate = rangeDate.clone
end
magicDay = (startDate.month == 2) && (startDate.day == 29) #This
is the real problem: Febraury 29th!
startDate += 1 if !endDate.leap? && magicDay
elapsedYears = endDate.year - startDate.year
previousStartDate = Date.new(endDate.year, startDate.month,
startDate.day)
if endDate < previousStartDate
elapsedYears -= 1
previousStartDate = Date.new(endDate.year - 1, startDate.month,
startDate.day)
previousStartDate -= 1 if previousStartDate.leap? && magicDay
end
elapsedDays = endDate - previousStartDate
return elapsedYears, elapsedDays
end
end

elapsedYears, elapsedDays =
Date.today.elapsedYearsAndDays(Date.new(1963, 11, 22))
daysMessage = (elapsedDays > 0) ? (" and #{elapsedDays} day(s)") : “”
puts “Elapsed time: #{elapsedYears} year(s)#{daysMessage}”

elapsedYears, elapsedDays = Date.new(1963, 11,
22).elapsedYearsAndDays(Date.new(1963, 11, 22))
daysMessage = (elapsedDays > 0) ? (" and #{elapsedDays} day(s)") : “”
puts “Elapsed time: #{elapsedYears} year(s)#{daysMessage}”

elapsedYears, elapsedDays = Date.new(2007, 3,
1).elapsedYearsAndDays(Date.new(2004, 3, 1))
daysMessage = (elapsedDays > 0) ? (" and #{elapsedDays} day(s)") : “”
puts “Elapsed time: #{elapsedYears} year(s)#{daysMessage}”

elapsedYears, elapsedDays = Date.new(2004, 3,
1).elapsedYearsAndDays(Date.new(2001, 3, 2))
daysMessage = (elapsedDays > 0) ? (" and #{elapsedDays} day(s)") : “”
puts “Elapsed time: #{elapsedYears} year(s)#{daysMessage}”

elapsedYears, elapsedDays = Date.new(2004, 2,
29).elapsedYearsAndDays(Date.new(2001, 3, 1))
daysMessage = (elapsedDays > 0) ? (" and #{elapsedDays} day(s)") : “”
puts “Elapsed time: #{elapsedYears} year(s)#{daysMessage}”

elapsedYears, elapsedDays = Date.new(2004, 3,
1).elapsedYearsAndDays(Date.new(2001, 3, 1))
daysMessage = (elapsedDays > 0) ? (" and #{elapsedDays} day(s)") : “”
puts “Elapsed time: #{elapsedYears} year(s)#{daysMessage}”

elapsedYears, elapsedDays = Date.new(2004, 2,
29).elapsedYearsAndDays(Date.new(2009, 3, 1))
daysMessage = (elapsedDays > 0) ? (" and #{elapsedDays} day(s)") : “”
puts “Elapsed time: #{elapsedYears} year(s)#{daysMessage}”


#19

I think that this does the right thing:

rick@frodo:/public/rubyscripts$ cat datemath.rb
#!/usr/math/bin/ruby

require ‘date’
class Date

return the number of days since the beginning of the year

def years_since(date)
# The parens in the expression below aren’t strictly necessary, but
# I think it makes what’s going on a little bit clearer.
first, last = *(self >= date ? : [self, date])
(self <=> date) * ((last.year - first.year) - (first.yday >
last.yday ? 1 : 0))
end
end

def tryit(d1,d2)
puts “There are #{d1.years_since(d2)} years between #{d2} and #{d1}”
end

tryit(Date.new(2007,3,15), Date.new(2000,3,14))
tryit(Date.new(2000,3,14), Date.new(2007,3,15))
tryit(Date.new(2007,3,15), Date.new(2000,3,15))
tryit(Date.new(2007,3,15), Date.new(2000,3,16))
tryit(Date.new(2000,3,16), Date.new(2007,3,15))

tryit(Date.new(2007,2,27), Date.new(2000,2,29))
tryit(Date.new(2000,2,29), Date.new(2000,2,27))
tryit(Date.new(2007,2,28), Date.new(2000,2,29))
tryit(Date.new(2000,2,29), Date.new(2007,2,28))
tryit(Date.new(2000,2,29), Date.new(2007,3,1))
tryit(Date.new(2007,3,1), Date.new(2000,2,29))
tryit(Date.new(2007,3,2), Date.new(2000,2,29))
tryit(Date.new(2000,2,29), Date.new(2007,3,2))

rick@frodo:/public/rubyscripts$ ruby datemath.rb
There are 7 years between 2000-03-14 and 2007-03-15
There are -7 years between 2007-03-15 and 2000-03-14
There are 6 years between 2000-03-15 and 2007-03-15
There are 6 years between 2000-03-16 and 2007-03-15
There are -6 years between 2007-03-15 and 2000-03-16
There are 6 years between 2000-02-29 and 2007-02-27
There are 0 years between 2000-02-27 and 2000-02-29
There are 6 years between 2000-02-29 and 2007-02-28
There are -6 years between 2007-02-28 and 2000-02-29
There are -7 years between 2007-03-01 and 2000-02-29
There are 7 years between 2000-02-29 and 2007-03-01
There are 7 years between 2000-02-29 and 2007-03-02
There are -7 years between 2007-03-02 and 2000-02-29


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/


#20

On Fri, Mar 16, 2007 at 04:28:16AM +0900, Rick DeNatale wrote:

I think it makes what’s going on a little bit clearer.

first, last = *(self >= date ? : [self, date])
(self <=> date) * ((last.year - first.year) - (first.yday >
last.yday ? 1 : 0))
end
end

I guess the most generic solution would return the number of [years,
months,
days] between two dates - so you can say “You are 31 years, 3 months and
4
days old”

Anyone can work out their own age in this format, so the algorithm
should be
clear, albeit probably messy to implement.