[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

age calculation

Posted on 2007-10-19
26
Medium Priority
?
1,842 Views
Last Modified: 2013-11-13
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
0
Comment
Question by:dkim18
  • 14
  • 6
  • 5
  • +1
26 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 20114200
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

by:fridom
ID: 20114262
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
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 20114278
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
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 60

Expert Comment

by:Geert Bormans
ID: 20114321
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

by:dkim18
ID: 20132584
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

by:dkim18
ID: 20132656
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

by:bobbit31
ID: 20141344
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

by:dkim18
ID: 20142170
this line doesn't prevent from getting negative number.

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

Expert Comment

by:bobbit31
ID: 20142866
this part takes care of it

(day_diff < 0 ? 1 : 0)
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 20142959
@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

by:Geert Bormans
ID: 20143034
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

by:Geert Bormans
ID: 20149914
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

by:Geert Bormans
ID: 20150320
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

by:bobbit31
ID: 20150510
Geert, if you get a chance, could you please show me a failing test case to my age calc?
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 20150767
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

by:Geert Bormans
ID: 20150803
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

by:bobbit31
ID: 20151075
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

by:Geert Bormans
ID: 20151515
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
 
LVL 18

Expert Comment

by:bobbit31
ID: 20151661
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

by:dkim18
ID: 20156518
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

by:Geert Bormans
ID: 20156616
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

by:
Geert Bormans earned 2000 total points
ID: 20156731
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

by:dkim18
ID: 20156808
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

by:Geert Bormans
ID: 20157200
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

by:dkim18
ID: 20157205
ok, that is good enough. I will give you points. Again, thanks for your help on this!
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 20158148
welcome
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I recently rediscovered rails when I needed a holiday project and decided to build a management dashboard for the company where I work.  With it being a project done in my free time, I could focus my time on learning the basics rather than trying to…
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…
Please read the paragraph below before following the instructions in the video — there are important caveats in the paragraph that I did not mention in the video. If your PaperPort 12 or PaperPort 14 is failing to start, or crashing, or hanging, …
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Suggested Courses
Course of the Month18 days, 19 hours left to enroll

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question