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   Sun, 21 Oct 2012 11:08:58 +0100

which my code had > and should have had >= for birth dates of 29
February and later dates of 28 February,

I'll send the files to participants in the thread and in due course
(after some further testing) to Kit Baum for posting on SSC.

On Sun, Oct 21, 2012 at 5:54 AM, Phil Clayton
<philclayton@internode.on.net> wrote:
> 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/


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