Calculate people's ages or similar daily date differences
personage bdatevar cdatevar [if exp] [in range] , generate(yearsvar [daysvar [loyvar]])
personage bdatevar [if exp] [in range] , currdate(current_date) generate(yearsvar [daysvar [loyvar]])
Description
personage is designed in the first instance for calculations of people's ages from data on their birth date and some "current" daily date. Depending on what is specified, a new variable is generated containing age in (completed) years, or the number of anniversaries of that person's original date of birth; and new variables may be generated containing (1) time since last birthday in days and (2) length of the current year.
There are two syntaxes. In the first, the user supplies two daily date variables, the first of which is birth date bdatevar and the second of which is some "current" date cdatevar. As is usual, such variables may be genuinely variable, taking on different values in different observations.
In the second syntax, the user supplies a daily date variable, which is taken to be the birth date bdatevar, and also through the currdate() option an expression defining the current daily date.
Although calculating ages of people is the motivating problem, nothing stops application to any problem requiring completed years, and optionally extra days, as a representation of the difference between two daily dates (including differences of either positive or negative sign). Descriptions here implying birth dates and current dates are for concreteness and do not exclude other applications.
Remarks
Most differences between dates and/or times are just a matter of subtraction. Daily date differences have an extra twist because of the two-fold complications caused by leap years, namely that years may be 365 or 366 days long and that 29 February occurs only in leap years. In personage people born on 29 February are deemed to have a virtual birthday on 28 February in non-leap years. The approximation that years average 365.25 days is often used in statistical computing, but this program offers an "exact" calculation.
personage warns if any variable specified has a format that does not start with "%d" or "%td".
Users wanting a string representation combining years and days can just concatenate afterwards.
personage deliberately is shy of offering a computed fraction of year for time since the last birthday (anniversary), leaving it to users to decide how to define and store such fractions. Note in particular for replicable results that fractions will differ slightly as between storage in float and double variables. If you do not understand this, search precision for material explaining why.
personage is not suitable for date-times measured in milliseconds. Use dofc() or dofC() as appropriate first to convert to daily dates.
Options
currdate() provides a specification of the "current" daily date(s) as numeric value(s) through a defining expression. Commonly, but not necessarily, this will be an expression defining a constant such as "mdy(10, 20, 2012)". currdate() is required as an option with the second syntax and not allowed with the first syntax.
generate() specifies one, two or three new variable names. In the first case, a new variable is generated containing age as number of completed years. In the second case, two new variables are generated containing age as number of completed years and number of days since last birthday. In the third case, three new variables are generated containing age as number of completed years, number of days since last birthday, and length of year so far, or number of days in the year that ends with the next birthday. This last, which will be 365 or 366, is for users wishing to carry out further calculations, most simply of fraction of year elapsed since the last birthday. generate() is a required option.
Examples
. clear . mat values = (28, 19, 28, 29, 29, 29\3, 11, 2, 2, 2, 2\1952, 1952, 2011, 2012, 2012, 1996) . 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 -2 . replace cdate = mdy(8, 31, 2013) in L . format bdate cdate %td . personage bdate cdate, gen(age1 days1) . personage bdate, currdate(mdy(12,31,2012)) gen(age2 days2) . list
Author
Nicholas J. Cox, Durham University, U.K. n.j.cox@durham.ac.uk
Acknowledgments
A question from José Maria Pacheco de Souza, Universidade de São Paulo, rekindled my interest in this problem. Phil Clayton, Mike Corbett and Alison Smith all found embarrassing bugs.