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   Nick Cox <njcoxstata@gmail.com>
To   statalist@hsphsun2.harvard.edu
Subject   Re: st: how to get age in number of years, months and days
Date   Sat, 20 Oct 2012 11:37:46 +0100

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/


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