Notice: On April 23, 2014, Statalist moved from an email list to a forum, based at statalist.org.
[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 <[email protected]>
To
[email protected]
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 <[email protected]> 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
> <[email protected]> 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/