Solved

# age calculation

Posted on 2007-10-19
1,828 Views
Hi experts,

I am trying to calculate age in rails.

I did something like this, but I know I am not considering leap years/months.
+++++++++++
class AttendingIp < ActiveRecord::Base
def age

age = age - 1
end

end

end

++++++
I went through ruby api, but didn't find solution. Is there nice way to do this in RoR?

0
Question by:dkim18

LVL 60

Expert Comment

I am not aware of the age calculating functionality in Ruby
but you could make the date class do all the calculation work for you
The date class takes care of all the leap year calculations

If you have two date objects (now and born)
this would calculate the year difference

y_dif = now.year - born.year
if Date.new(now.year - year, now.month, now.day) < born
y_dif = y_dif - 1
end

It calculates the difference between the years, checks if subtracting does create a dat prior to the born date
All your leap year calculus is taken into account by the date substraction

I will work on the other two later today
months and days can be calculated in a similar fashion (be it a little bit more complex)
0

LVL 24

Expert Comment

require "rubygems"
require "active_support"

It will add a functinality to date-calculations and conversions to Time and so you can
write things like

Time.now - 6.months

and the like I'm sure it does add quite a bit for data calculations also

Regards
Friedrich
0

LVL 60

Expert Comment

this calculates the month difference correctly

m_dif = now.month - born.month
if m_dif < 0
m_dif = m_dif + 12
end
if Date.new(born.year, born.month, now.day) < born
m_dif = m_dif - 1
end
0

LVL 60

Expert Comment

mmmh, I am not convinced ActiveSupport gives you what you need here
but it has some months_since and years_since methods
you could use them in some sort of jackhammer approach,
counting back days until you hit the birthday
maybe that is better
0

Author Comment

in the following line:

if Date.new(now.year - year, now.month, now.day) < born

what the 'year" (the second year by itself)  is for?
0

Author Comment

ok, this seems working:

y_dif = now.year - born.year
if Date.new(now.year, now.month, now.day) < born
y_dif = y_dif - 1
end

+++++++++++

I am doing days difference and I am not sure in this case:

d_dif = now.day - born.day
if d_dif < 0
d_dif = d_dif + 31 # ---> adding 31 would work?
0

LVL 18

Expert Comment

here's the one i use:

day_diff = Date.today.day - born.day
month_diff = Date.today.month - born.month - (day_diff < 0 ? 1 : 0)
Date.today.year - born.year - (month_diff < 0 ? 1 : 0)
0

Author Comment

this line doesn't prevent from getting negative number.

day_diff = Date.today.day - born.day
0

LVL 18

Expert Comment

this part takes care of it

(day_diff < 0 ? 1 : 0)
0

LVL 60

Expert Comment

@bobbit31,

> (day_diff < 0 ? 1 : 0)

that part takes care of substracting a month or not, it doesn't make day_diff positive
I am afraid that allthough the ruby code is clean, your approach is a bit naieve
eg.
month_diff = Date.today.month - born.month - (day_diff < 0 ? 1 : 0)
if Date.today.month - born.month = 0 then (day_diff < 0 ? 1 : 0) makes month_diff = -1, where it should be 11 and year should be decremented by 1
this way you end up pretty soon in a code as the one dkim18 started from

@dkim,

you are right, I made a copy and paste error in my year calculation
here is the correct one
y_dif = now.year - born.year
if Date.new(now.year - y_dif, now.month, now.day) < born
y_dif = y_dif - 1
end

there is bug in my month calculus too
I will check that tomorrow
0

LVL 60

Expert Comment

here is month corrected (I think)
It works on all the border cases I tested it with

m_dif = now.month - born.month
if m_dif < 1
m_dif = m_dif + 12
end
if Date.new(born.year, born.month, now.day) < born
m_dif = m_dif - 1
end
m_dif = (m_dif == 12 ? 0 : m_dif)

but this is clumsy.
tomorrow with a fresh mind I will clean this up

cheers

Geert
0

LVL 60

Expert Comment

Here is how I would deal with day

close_date = born >>(m_dif + 12 * y_dif)

d_dif = 0
while close_date < now
d_dif = d_dif + 1
close_date = close_date+(1)
end

puts d_dif

That is pure jackhammer approach, but it doesn't hurt, it is max. 31 iterations
This is debatable, if you are born on 31-DEC-2006 (you would not be programming this)
you would be nine months old and a number of days.
But 31-DEC-2006 plus nine months is correctly calculated as 30-SEP-2007
and are you then 9 months plus 25 dys or 9 months and 24days
I leave that filosophic note for yourself

I think, that I will use the jackhammer approach to calculate year and month as well,
I would be a lot cleaner, and maybe not considerably slower
More in an hour

Geert
0

LVL 60

Expert Comment

life is so much simpler this way

I use the Date library and not the date functions of ActiveSupport, since the latter don't support leapyears etc.

require "date"

now = Date.today
born = Date.new(1967, 5, 8)

m_tot = 0
born_copy = born
while born_copy < now
m_tot = m_tot + 1
born_copy = born_copy>>(1)
end
y_dif,m_dif = (m_tot-1).divmod(12)

close_date = born >>(m_dif + 12 * y_dif)
d_dif = 0
while close_date < now
d_dif = d_dif + 1
close_date = close_date+(1)
end

puts y_dif, m_dif, d_dif

I jackhammer the results, and that is reasonably fast, considering that you will likely not deal with ages over a couple of hundred years
Note that you will have difficulties if you would, since this is based on gregorian calendar

Sorry I did not come up with this easy approach so much earlier
I hope this one works

Geert
0

LVL 18

Expert Comment

Geert, if you get a chance, could you please show me a failing test case to my age calc?
0

LVL 60

Expert Comment

given today is 2007-10-25
born = Date.new(1967, 5, 30)
makes your day difference fail (it gives -5 days where it should say 25 days)
and you can't simply say it is 30-5, because it could as well be 28-5 or 31-5 (- being minus here)

if today were 2007-1-1
and  born=Date.new(2006,12,31)
my method says 0 years, 0 months, 1 day
your method says 0 years, -12 months, -30 days

I hope this helps
cheers

Geert

0

LVL 60

Expert Comment

using my method, you should add a test to assure that born date is not later than today
(or you blow up your server if someone wants to be funny)
0

LVL 18

Expert Comment

right but in the end... we're only talking about years... and the result of mine is still the expected 40.

when i asked for a failing test case, i meant one that reports the actual age wrong.

given today 2007-10-25 and date 1967-05-30:

day_diff = Date.today.day - born.day
=> -5
the actual result doesn't matter, i'm just looking for positive / negative (negative meaning i was born after today's day)

month_diff = Date.today.month - born.month - (day_diff < 0 ? 1 : 0)
=> 10 - 5 - 1
=> 4
again, just looking for a positive or negative

Date.today.year - born.year - (month_diff < 0 ? 1 : 0)
=> 2007 - 1967 - 0
=> 40

0

LVL 60

Expert Comment

OK, I see, I think we have a misunderstanding
I know that your year difference calculation is right, that is not my argument

I assumed that dkim18 needed the time difference in year, months and days, given that in his example he calculated month and day difference even after he calculated the year difference
> this line doesn't prevent from getting negative number.
> day_diff = Date.today.day - born.day
I strongly believe my first assumption is right
but that is up to dkim18 to comment upon

Note also that the year difference problem was solved prior to your post
(and stated to be solved by dkim18 in a post 45 minutes before your first post)

so, no offence but I think your answer raised the wrong expectations
(at least with me)

cheers

Geert
0

LVL 18

Expert Comment

no offence taken... i just had a short and concise solution on hand and decided to post it... the only reason i continued on to ask for a failing test was to see if i personally shouldn't be using the code i posted ;)
0

Author Comment

thanks for all your efforts. Gertone's method works, but this age calculation might be performed for hundred of people and it is taking forever in that case. Of course for a few people, I get instant result. So Gertone's jackhammer approach is not efficient this case.

I am just wondering since backend DB is oracle, PL/SQL can do this a lot easily?
0

LVL 60

Expert Comment

If you really need to do this for hundreds of people...
The jackhammer is only slow for years
so use this for years

y_dif = now.year - born.year
if Date.new(now.year - y_dif, now.month, now.day) < born
y_dif = y_dif - 1
end

and use the jackhammer for months (max 12 iterations)
and use the jackhammer for days (max 30 iterations)

I don't know about the pl/sql
0

LVL 60

Accepted Solution

here is the code, that should already be a lot faster

y_dif = now.year - born.year
if Date.new(now.year - y_dif, now.month, now.day) < born
y_dif = y_dif - 1
end

m_tot = 0
almost_close_date = Date.new(born.year + y_dif, born.month, born.day)
while almost_close_date < now
m_tot = m_tot + 1
almost_close_date = almost_close_date>>(1)
end
m_dif = m_tot - 1

close_date = born >>(m_dif + 12 * y_dif)
d_dif = 0
while close_date < now
d_dif = d_dif + 1
close_date = close_date+(1)
end

puts y_dif, m_dif, d_dif

If this is still too slow,
you could make the day count recursive and split day differences in two (such as in bubble sort :-)

but before you go down that route, check pl/sql first
using the ruby code is less database dependent of course

cheers

Geert
0

Author Comment

hold on....I found there is dob format something like this:

0087-10-13 00:00:00.0

It looks like someone entered last two digit only and previous program didn't check format properly.
0

LVL 60

Expert Comment

try this
puts "0087-10-13 00:00:00.0".gsub(/^(\d{2})(\d{2})(.*)\$/){ |str|
(\$2.to_f > 25 ? "19" : "20" ) + \$2 + \$3
}
0

Author Comment

ok, that is good enough. I will give you points. Again, thanks for your help on this!
0

LVL 60

Expert Comment

welcome
0

## Featured Post

### Suggested Solutions

If you've ever programmed in Ruby and have come across either a proc or a lambda, you might have been wondering what the difference is between the two and when you would use one over the other. This article will try to explain the difference betweenâ€¦
In Ruby, Call or invoke a API DLL library is easily via Win32API class, win32-api gem or other gems. For general DLL API call, there are quite a few references, some good tips list below: http://www.rubytips.org/2008/05/13/accessing-windows-api-froâ€¦
Migrating to Microsoft Office 365 is becoming increasingly popular for organizations both large and small. If you have made the leap to Microsoftâ€™s cloud platform, you know that you will need to create a corporate email signature for your Office 365â€¦
This video is in connection to the article "The case of a missing mobile phone (https://www.experts-exchange.com/articles/28474/The-Case-of-a-Missing-Mobile-Phone.html)". It will help one to understand clearly the steps to track a lost android phone.