Link to home
Start Free TrialLog in
Avatar of dkim18
dkim18

asked on

age calculation

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 = Date.today.year - read_attribute(:dob).year
     if Date.today.month < read_attribute(:dob).month ||
     (Date.today.month == read_attribute(:dob).month && read_attribute(:dob).day >= Date.today.day)
      age = age - 1    
      end
     
        month = Date.today.month - read_attribute(:dob).month
      if(Date.today.month < read_attribute(:dob).month)
       month = 12 - read_attribute(:dob).month
      end
     
       day = Date.today.day - read_attribute(:dob).day
      if(Date.today.day < read_attribute(:dob).day)
       day = 31 - read_attribute(:dob).day
      end

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

thanks in advance
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

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)
How about:
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
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
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
Avatar of dkim18
dkim18

ASKER

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?
Avatar of dkim18

ASKER

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?
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)
Avatar of dkim18

ASKER

this line doesn't prevent from getting negative number.

day_diff = Date.today.day - born.day
this part takes care of it

(day_diff < 0 ? 1 : 0)
@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
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
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
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
Geert, if you get a chance, could you please show me a failing test case to my age calc?
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

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)
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


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
Given his comment about your day calculation
> 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
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 ;)
Avatar of dkim18

ASKER

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?
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
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of dkim18

ASKER

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.
try this
puts "0087-10-13 00:00:00.0".gsub(/^(\d{2})(\d{2})(.*)$/){ |str|
      ($2.to_f > 25 ? "19" : "20" ) + $2 + $3
}
Avatar of dkim18

ASKER

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