I would have expected that when running below code....
data sample;
input id $ dob:date9.;
format dob curr_date date9.;
do curr_date='27Feb2020'd,'28Feb2020'd,'29Feb2020'd;
age=floor(yrdif(dob,curr_date,'age'));
output;
end;
datalines;
A9991 28Feb2004
A9992 29Feb2004
;
proc print data=sample;
run;
...obs 5 would return an age of 15.
I've done some Internet searching but couldn't really find a good explanation why it's 16.
Is that a "bug" in the function or is there some logical explanation/convention why the age becomes already 16 on Feb28?
I would have the same expectation as you when using the 'AGE' basis (i.e. DOB=29FEB2004 would have an age of 15 on 28feb2020). I'd be very interested to learn of a justification for a result of 16.
But you can get 15 for that situation when using the "ACT/ACT" basis:
data sample;
input id $ dob:date9.;
format dob curr_date date9.;
do curr_date='27Feb2020'd,'28Feb2020'd,'29Feb2020'd;
age=floor(yrdif(dob,curr_date,'act/act'));
output;
end;
datalines;
A9991 28Feb2004
A9992 29Feb2004
;
proc print data=sample;
run;
Obs | id | dob | curr_date | age |
---|---|---|---|---|
1 | A9991 | 28FEB2004 | 27FEB2020 | 15 |
2 | A9991 | 28FEB2004 | 28FEB2020 | 16 |
3 | A9991 | 28FEB2004 | 29FEB2020 | 16 |
4 | A9992 | 29FEB2004 | 27FEB2020 | 15 |
5 | A9992 | 29FEB2004 | 28FEB2020 | 15 |
6 | A9992 | 29FEB2004 | 29FEB2020 | 16 |
This is convention. People born on a 29th of February celebrate their birthday on the 28th in non-leap years; law also respects this.
@Kurt_Bremser wrote:
This is convention. People born on a 29th of February celebrate their birthday on the 28th in non-leap years; law also respects this.
My sample code is for a leap year for someone born in a leap year - and that's why I'm wondering why the age already increases on Feb 28 and not only on Feb 29.
Overlooked that.
I see two explanations:
@Kurt_Bremser wrote:
Overlooked that.
I see two explanations:
- it's an artifact of the underlying calculation
- there is some legalese in the US which considers all people born on a 29th to have been born on the 28th, regardless of year
I'd be disappointed if it is really a calculation artifact. I hope it's actually due to some technical/legal instruction on age determination for people born on the 29th of February. Otherwise, the 'age' method for YRDIF is inexplicably at odds with the "continuous" method for INTCK. For instance, try using
age=intck('year',dob,curr_date,'continuous');
which reproduces the intuitively expected results using the "ACT/ACT" basismethod in YRDIF.
@Patrick - maybe it's worth asking SAS support to find out what reference SAS used in constructing YRDIF with method=age.
Edited addition: Of course, if the YRDIF use of the "age" basis does actually adhere to some legalistic definition of age for births on Feb 29, then that would mean using the "continuous" method for INTCK (that I've been using for years) to calculate age is wrong.
hi @Patrick
This is a very interesting discussion, because there seems to be a lot of confusion about leapling's birthday, There are diffrerent conflicting rules, or no rules at all.
There is a lot of stuff on www on this. A search on situations where it really matters to people, like "born on february 29 when do you celebrate your birthday" or "born on february 29 when can you legally drink" gives a lot of answers, some of which seems highly competent, but they only add to the confusion. It seems that a leapling cannot drink before march 1 in the year she turns 21, but her driver's licence expires on February 28. I recommend this: https://bgwlawyers.com/leaping-through-time-the-leap-year-enigma/
I had the problem with a SAS function that returns age from birthday. In a municipal administration different departments seems to have different rules for determining age, so a leapling can get into a public kindergarten from february 1, but cannot vote in an election held on february 28.
It is a mess, and it can have severe consequences in some cases, e.g. applying for citizenship before a given age. I have even heard of a case here in Denmark where a person was denied a test for a driver's licence because the police found that she was only 4 years old, as her birthday on february 29 had only reoccurred 4 times since her birth.
But one cannot really blame SAS for not implementing consistent rules in different functions when there are no consistent rules to implement, and there are so many date functions that you can always choose one that returns the right answer for your specific purpose.
If you are making this computation for yourself, you can lay down your own rules, but if you are preparing data to end users, I can only suggest that you ask them to decide which day they prefer. They might find it hard to come up with an authoritative answer, but then it's not your problem any more.
Hi,
I did some experiments, try and errors, and it looks like yrdif() function with 'age' works like this:
options ls=max;
proc fcmp outlib=work.f.p;
function leap(year);
return (
(mod(year,4)=0 and mod(year, 100)^=0) or mod(year,400)=0
);
endsub;
run;
options cmplib=work.f nosource;
data test;
length z 8; /*test if "by hand" = yrdif() */
do dob = '25feb2015'd /*to '4mar2015'd, '25feb2016'd*/ to '4mar2019'd;
do curr_date = dob /*to '5mar2024'd, '25feb2025'd */ to '5mar2045'd;
/*- "by-hand approach" -----------------------------------------------------*/
leaps=0;
do yy = year(dob) to year(curr_date);
leaps+leap(yy);
end;
dobMD = 100*month(dob)+day(dob);
cdtMD = 100*month(curr_date)+day(curr_date);
a=0;
select;
when (leap(year(dob)) AND leap(year(curr_date)))
do;
leaps+(-1);
leaps=max(leaps,0);
a = ifn(dobMD<229
,ifn(229<=cdtMD,-leaps-1,-leaps )
,ifn(229<=cdtMD,-leaps ,-leaps+1)
);
end;
when ( leap(year(dob)) ) a = ifn(dobMD<=228,-leaps ,-leaps+1);
when ( leap(year(curr_date)) ) a = ifn(cdtMD<=228,-leaps+1,-leaps );
otherwise a=-leaps;
end;
x = (curr_date - dob + a) / 365;
/*-------------------------------------------------------------------------*/
/* SAS function */
y = yrdif(dob,curr_date,'age');
/* comparisons */
z = (round(x,1e-9)=round(y,1e-9));
if not Z then output;
end;
end;
format dob curr_date yymmdd10.;
run;
log:
NOTE: The data set WORK.TEST has 0 observations and 10 variables. NOTE: DATA statement used (Total process time): real time 15.11 seconds cpu time 15.12 seconds
Bart
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.