Bookmark and Share

Notice: On March 31, it was announced that Statalist is moving from an email list to a forum. The old list will shut down on April 23, and its replacement, statalist.org is already up and running.


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: st: how to get age in number of years, months and days


From   Phil Clayton <philclayton@internode.on.net>
To   statalist@hsphsun2.harvard.edu
Subject   Re: st: how to get age in number of years, months and days
Date   Sun, 21 Oct 2012 15:54:11 +1100

I've been playing with something like this (beware wrapped lines):

----------------
clear
input str9 d1 str9 d2
	24oct1980 19oct2012
	30sep1954 5aug1989
	31oct1974 10sep2002
	31oct1974 30oct2002
	31mar1985 30mar1995
	29feb2000 28feb2004
	29feb2000 1mar2004
end

gen date1=date(d1, "DMY")
gen date2=date(d2, "DMY")
format %td date1 date2

gen years=floor(((ym(year(date2), month(date2)) - ym(year(date1), month(date1))) - (day(date2) < day(date1))) / 12)
gen months=(ym(year(date2), month(date2)) - ym(year(date1), month(date1))) - (day(date2) < day(date1)) - 12*years
gen days=date2 - (dofm(mofd(date1) + 12*years + months) + day(date1) - 1)

list
----------------

Of course handling leap years and birthdays on 29 Feb is a little arbitrary. The code for calculating age in years is based on Dan Blanchette's code at the end of this page:
http://www.ats.ucla.edu/stat//stata/modules/dates.htm

I think Nick's approach of calculating years and days is probably better. However, Nick's code seems to fail with the second last set of dates above.

Phil

On 20/10/2012, at 9:37 PM, Nick Cox <njcoxstata@gmail.com> wrote:

> A better answer would be to do it properly, except that as earlier
> said I suggest that years and days is a better way to go.
> 
> Here is some code. It has not been very much tested. It is a bit
> messy, because it needs to be general enough to deal with leap years
> and with persons born on February 29 (two related but not identical
> problems).
> 
> Some notes:
> 
> 1. -personage- expects input of daily date variables and warns if it
> does not find a format that implies one such.
> 
> 2. Although age of person is the motivating problem, nothing stops
> application to any problem requiring years and days as a
> representation of the difference between two daily dates (including
> differences of positive or negative sign).
> 
> 3. Users wanting a string representation can just concatenate afterwards.
> 
> 4. People born on 29 February are deemed to have a virtual birthday on
> 28 February in non-leap years.
> 
> 5. I wondered about using the -doy()- function instead, but a gut
> feeling is that makes the code no easier on balance. I will look at
> that in due course.
> 
> 6. Watch out for long lines if tempted to copy this and play with it.
> 
> *! 1.0.0 NJC 20 October 2012
> program personage
> 	version 8.2
> 	syntax varlist(numeric min=2 max=2) [if] [in] , Generate(str)
> 
> 	tokenize `varlist'
> 	args bdate cdate
> 
>                marksample touse
>                qui count if `touse'
>                if r(N) == 0 error 2000
> 
> 	foreach v in `bdate' `cdate' {
> 		local fmt : format `v'
> 		if substr("`fmt'", 1, 2) != "%d" {
> 			if substr("`fmt'", 1, 3) != "%td" {
> 				di "warning: `v' not formatted as daily date"
> 			}
> 		}
> 	}
> 			
> 	tokenize `generate'
> 	args yearsvar daysvar garbage
> 	if "`garbage'" != "" {
> 		di as err "at most two names should be given in generate()"
> 		exit 198
> 	}
> 	if "`daysvar'" != "" {
> 		confirm new variable `daysvar'
> 	}
> 	confirm new variable `yearsvar'
> 
> 	tempvar work
> 	local bday_this_cal_yr ///
> 	(month(`bdate') < month(`cdate')) | (month(`bdate') == month(`cdate')
> & day(`bdate') <= day(`cdate'))
> 
> 	quietly {
> 		// first focus on calculating last birthday
> 
> 		// 1. last b'day earlier this year if current date is as late or
> later in year
> 		gen `work' = mdy(month(`bdate'), day(`bdate'), year(`cdate')) if
> `bday_this_cal_yr'
> 
> 		// 2. else it was last year
> 		replace `work' = mdy(month(`bdate'), day(`bdate'), year(`cdate') -
> 1) if missing(`work')
> 
> 		// but 1. won't work if born Feb 29 and it's not a leap year
> 		//     2. won't work if born Feb 29 and last year not a leap year
> 		local born_feb29 month(`bdate') == 2 & day(`bdate') == 29
> 		local this_not_leap missing(mdy(2, 29, year(`cdate')))
> 		local last_not_leap missing(mdy(2, 29, year(`cdate') - 1))
> 
> 		// 3. is a fix for problem with 1.
> 		replace `work' = mdy(2, 28, year(`cdate')) if `this_not_leap' &
> `born_feb29' & `cdate' >= mdy(2, 28, year(`cdate'))
> 		// 4. is a fix for problem with 2.
> 		replace `work' = mdy(2, 28, year(`cdate') - 1) if `last_not_leap' &
> `born_feb29' & `cdate' < mdy(2, 28, year(`cdate'))
> 
> 		// now we can calculate results
> 
> 		gen `yearsvar' = year(`work') - year(`bdate') if `touse'
> 		if "`daysvar'" != "" {
> 			gen `daysvar' = `cdate' - `work'  if `touse'
> 		}
> 
> 		compress `yearsvar' `daysvar'
> 	}
> end
> 
> Here is a sample test script
> 
> clear
> 
> mat values = (28, 19, 28, 29, 29\3, 11, 2, 2, 2\1952, 1952, 2011, 2012, 2012)
> set obs `=colsof(values)'
> gen bdate = mdy(values[2, _n], values[1, _n], values[3, _n])
> gen cdate = mdy(10,19,2012)
> replace cdate = mdy(2, 29, 2012) in L
> format bdate cdate %td
> 
> personage bdate cdate, gen(age_y age_d)
> 
> list
> 
> 
> On Fri, Oct 19, 2012 at 12:21 AM, Nick Cox <njcoxstata@gmail.com> wrote:
>> I am not aware that you are missing something. My guess is that this
>> is too awkward to be usable and too fiddly to be an amusing
>> programming problem.
>> 
>> In particular, it doesn't sound an easy thing to work with given that
>> months are of unequal length. Even years and days sounds a little
>> tricky.
>> 
>> Most people seem satisfied with the difference in dates divided by
>> 365.25. I've often seen one-liners giving that.
>> 
>> To spell out the obvious, you'd need to treat leap years carefully and
>> in particular anyone born on 29 February.
>> 
>> There should be a few such people on this list; what do you/they do?
>> Celebrate on 28 Feb or 1 March?
>> 
>> There is discussion of leap years at
>> 
>> FAQ     . . . . . . . . . . . . . . . . . . . . . . . . . Leap year indicators
>>        . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  N. J. Cox
>>        1/04    How do I identify leap years in Stata?
>>                http://www.stata.com/support/faqs/data/leapyear.html
>> 
>> Nick
>> 
>> On Thu, Oct 18, 2012 at 11:10 PM, jose maria pacheco de souza
>> <jmpsouza@usp.br> wrote:
>> 
>>> is there any user writen program that presents the differences between two
>>> dates DMY (today date and birthday date, say) as number of years, months and
>>> days?
>>> I tried the help (all three searchs), the dates and times help  and the pdf
>>> manual. Maybe I am missing something.
> *
> *   For searches and help try:
> *   http://www.stata.com/help.cgi?search
> *   http://www.stata.com/support/faqs/resources/statalist-faq/
> *   http://www.ats.ucla.edu/stat/stata/


*
*   For searches and help try:
*   http://www.stata.com/help.cgi?search
*   http://www.stata.com/support/faqs/resources/statalist-faq/
*   http://www.ats.ucla.edu/stat/stata/


© Copyright 1996–2014 StataCorp LP   |   Terms of use   |   Privacy   |   Contact us   |   Site index