From: Rick DeNatale Date: 2007-03-17T06:16:11+09:00 Subject: Re: Calculating the age given DOB On 3/16/07, Pit Capitain wrote: > Rick DeNatale schrieb: > > I think that this does the right thing: > > (...) > > tryit(Date.new(2007,3,1), Date.new(2004,3,1)) > tryit(Date.new(2004,3,1), Date.new(2001,3,2)) > > # => > > There are 2 years between 2004-03-01 and 2007-03-01 > There are 3 years between 2001-03-02 and 2004-03-01 Thanks, yep my bad! Here's a refined attempt. I've fixed that problem and I've also added support to specify a day to be used as the anniversary date for a leap-day. I did extensive research (on Wikipedia ) and it seems that: 1) People born on February 29 are called leaplings. 2) For legal purposes most jurisdictions consider 1 March to be the birthdate of a leapling in non-leap years for the purposes of determining legal age. 3) There are some jurisdictions, e.g. Taiwan which use 28 February instead. Although some leaplings try to pass themselves off as approximately 1/4 their legal age, I haven't made allowances for that in the following code: rick@frodo:/public/rubyscripts$ cat datemath.rb require 'date' class Date def leap_year? year % 400 == 0 || year % 100 != 0 && year % 4 == 0 end def leap_day? month = 2 && day == 29 end # The lyday is an altered yday. It is computed as if every year # was a leap year. It's purpose is to determine whether a date has # been 'virtually' crossed def lyday yday + ((leap_year? || yday < 60) ? 0 : 1) end # return the 'legal' anniversary day cooresponding to first in the year of last # This is the first date unless that date is leap day (29 February xxxx) and the # second date is within a leap year. # # The leapling date is 1 March, xxxx by default. The year of the leapling date is # ignored. def self.ly_adjust(first, second, leapling_date) if first.leap_day? && !second.leap_year? leapling_date ||= Date.new(2004,3,1) Date.new(first.year, leapling_date.month, leapling_date.day) else first end end # return the number of years since the given date. # leapling date is a date, (in any year) which is # considered the anniversary of February 29 in non leap years. # In most jurisdictions the legal birthday in non-leap years for determining # legal ages is March 1, which is the default, however some jurisdictions # legislate another date, most commonly February 28. def years_since(date,leapling_date = nil) first, last = *(self >= date ? [date, self] : [self, date]) first = Date.ly_adjust(first,last, leapling_date) (self <=> date) * ((last.year - first.year) - (first.lyday > last.lyday ? 1 : 0)) end end # The following methods demonstrate and test the above code. # These really should be Test::Unit test cases, but I think that this form works # better for showing what the code does as well as verification. # # tryit displays the number of years between an end date and a start date # for leapling_dates of nil, 1 March, and 28 February, # it prints each result, and returns an array of the three results. def tryit(start_date,end_date) leapling_dates = [nil] result = [] for ld in [nil, Date.new(2000,3,1), Date.new(2000,2,28)] puts "With leapling_date of #{ld}" if ld diff = end_date.years_since(start_date,ld) result << diff puts "There are #{diff} years between #{start_date} and #{end_date}" end puts result end # tryit2 takes two dates, and the array expected from tryit # It calls tryit, checks the result and prints an error message if # the results aren't as expected. # It then reverses the arguments which should result in negated # values of the expected results. def tryit2(start_date,end_date,expected) puts "***** Error *****" unless expected == tryit(start_date, end_date) expected = expected.map {|e| -e} puts "***** Error *****" unless expected == tryit(end_date, start_date) end tryit2(Date.new(2000,3,14), Date.new(2007,3,15), [7, 7, 7]) tryit2(Date.new(2000,3,15), Date.new(2007,3,15), [7, 7, 7]) tryit2(Date.new(2000,3,16), Date.new(2007,3,15), [6, 6, 6]) tryit2(Date.new(2000,2,29), Date.new(2007,2,27), [6, 6, 6]) tryit2(Date.new(2000,2,29), Date.new(2007,2,28), [6, 6, 7]) tryit2(Date.new(2000,2,29), Date.new(2007,3,1), [7, 7, 7]) tryit2(Date.new(2000,2,29), Date.new(2007,3,2), [7, 7, 7]) tryit2(Date.new(2004,3,1), Date.new(2007,3,1), [3, 3, 3]) rick@frodo:/public/rubyscripts$ ruby datemath.rb There are 7 years between 2000-03-14 and 2007-03-15 With leapling_date of 2000-03-01 There are 7 years between 2000-03-14 and 2007-03-15 With leapling_date of 2000-02-28 There are 7 years between 2000-03-14 and 2007-03-15 There are -7 years between 2007-03-15 and 2000-03-14 With leapling_date of 2000-03-01 There are -7 years between 2007-03-15 and 2000-03-14 With leapling_date of 2000-02-28 There are -7 years between 2007-03-15 and 2000-03-14 There are 7 years between 2000-03-15 and 2007-03-15 With leapling_date of 2000-03-01 There are 7 years between 2000-03-15 and 2007-03-15 With leapling_date of 2000-02-28 There are 7 years between 2000-03-15 and 2007-03-15 There are -7 years between 2007-03-15 and 2000-03-15 With leapling_date of 2000-03-01 There are -7 years between 2007-03-15 and 2000-03-15 With leapling_date of 2000-02-28 There are -7 years between 2007-03-15 and 2000-03-15 There are 6 years between 2000-03-16 and 2007-03-15 With leapling_date of 2000-03-01 There are 6 years between 2000-03-16 and 2007-03-15 With leapling_date of 2000-02-28 There are 6 years between 2000-03-16 and 2007-03-15 There are -6 years between 2007-03-15 and 2000-03-16 With leapling_date of 2000-03-01 There are -6 years between 2007-03-15 and 2000-03-16 With leapling_date of 2000-02-28 There are -6 years between 2007-03-15 and 2000-03-16 There are 6 years between 2000-02-29 and 2007-02-27 With leapling_date of 2000-03-01 There are 6 years between 2000-02-29 and 2007-02-27 With leapling_date of 2000-02-28 There are 6 years between 2000-02-29 and 2007-02-27 There are -6 years between 2007-02-27 and 2000-02-29 With leapling_date of 2000-03-01 There are -6 years between 2007-02-27 and 2000-02-29 With leapling_date of 2000-02-28 There are -6 years between 2007-02-27 and 2000-02-29 There are 6 years between 2000-02-29 and 2007-02-28 With leapling_date of 2000-03-01 There are 6 years between 2000-02-29 and 2007-02-28 With leapling_date of 2000-02-28 There are 7 years between 2000-02-29 and 2007-02-28 There are -6 years between 2007-02-28 and 2000-02-29 With leapling_date of 2000-03-01 There are -6 years between 2007-02-28 and 2000-02-29 With leapling_date of 2000-02-28 There are -7 years between 2007-02-28 and 2000-02-29 There are 7 years between 2000-02-29 and 2007-03-01 With leapling_date of 2000-03-01 There are 7 years between 2000-02-29 and 2007-03-01 With leapling_date of 2000-02-28 There are 7 years between 2000-02-29 and 2007-03-01 There are -7 years between 2007-03-01 and 2000-02-29 With leapling_date of 2000-03-01 There are -7 years between 2007-03-01 and 2000-02-29 With leapling_date of 2000-02-28 There are -7 years between 2007-03-01 and 2000-02-29 There are 7 years between 2000-02-29 and 2007-03-02 With leapling_date of 2000-03-01 There are 7 years between 2000-02-29 and 2007-03-02 With leapling_date of 2000-02-28 There are 7 years between 2000-02-29 and 2007-03-02 There are -7 years between 2007-03-02 and 2000-02-29 With leapling_date of 2000-03-01 There are -7 years between 2007-03-02 and 2000-02-29 With leapling_date of 2000-02-28 There are -7 years between 2007-03-02 and 2000-02-29 There are 3 years between 2004-03-01 and 2007-03-01 With leapling_date of 2000-03-01 There are 3 years between 2004-03-01 and 2007-03-01 With leapling_date of 2000-02-28 There are 3 years between 2004-03-01 and 2007-03-01 There are -3 years between 2007-03-01 and 2004-03-01 With leapling_date of 2000-03-01 There are -3 years between 2007-03-01 and 2004-03-01 With leapling_date of 2000-02-28 There are -3 years between 2007-03-01 and 2004-03-01 -- Rick DeNatale My blog on Ruby http://talklikeaduck.denhaven2.com/