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
dkim18Asked:
Who is Participating?
 
Geert BormansInformation ArchitectCommented:
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
 
Geert BormansInformation ArchitectCommented:
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
 
fridomCEO/ProgrammerCommented:
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
0
Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
Geert BormansInformation ArchitectCommented:
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
 
Geert BormansInformation ArchitectCommented:
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
 
dkim18Author Commented:
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
 
dkim18Author Commented:
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
 
bobbit31Commented:
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
 
dkim18Author Commented:
this line doesn't prevent from getting negative number.

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

(day_diff < 0 ? 1 : 0)
0
 
Geert BormansInformation ArchitectCommented:
@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
 
Geert BormansInformation ArchitectCommented:
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
 
Geert BormansInformation ArchitectCommented:
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
 
Geert BormansInformation ArchitectCommented:
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
 
bobbit31Commented:
Geert, if you get a chance, could you please show me a failing test case to my age calc?
0
 
Geert BormansInformation ArchitectCommented:
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
 
Geert BormansInformation ArchitectCommented:
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
 
bobbit31Commented:
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
 
Geert BormansInformation ArchitectCommented:
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
0
 
bobbit31Commented:
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
 
dkim18Author Commented:
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
 
Geert BormansInformation ArchitectCommented:
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
 
dkim18Author Commented:
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
 
Geert BormansInformation ArchitectCommented:
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
 
dkim18Author Commented:
ok, that is good enough. I will give you points. Again, thanks for your help on this!
0
 
Geert BormansInformation ArchitectCommented:
welcome
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.