You can perform that algorithm in a single pass (or two passes if you need to first calculate a reasonable MIN/MAX values for the date ranges).
First let's make some data that has non nested intervals and also multiple intervals for the same ID.
data have;
input ID (AdmitDate DischargeDate) (:mmddyy.);
format AdmitDate DischargeDate yymmdd10.;
cards;
1 1/1/2025 2/4/2025
1 1/3/2025 1/3/2025
1 1/17/2025 1/17/2025
2 3/4/2025 3/4/2025
3 6/7/2025 6/9/2025
3 6/8/2025 6/8/2025
4 1/1/2025 2/4/2025
4 1/17/2025 3/23/2025
5 1/1/2025 2/4/2025
5 3/1/2025 4/1/2025
;
Now let's find the min and max dates to help set the possible date range.
proc sql noprint;
select min(min(AdmitDate,DischargeDate))
, max(max(AdmitDate,DischargeDate))
into :mind trimmed
, :maxd trimmed
from have
;
quit;
%let length=%eval(&maxd-&mind+1);
%put &=length (From %sysfunc(putn(&mind,yymmdd10.)) to %sysfunc(putn(&maxd,yymmdd10.)));
You can skip this step if you already know what values to use for MIND and LENGTH.
Now for each subject use your DAY loop to flag those days and then find the begin/end of each set of flagged days.
Here is an example using a simple character variable. Doing this makes it easy to use the CALL SCAN routine to find the periods.
data periods ;
length days $&length.;
do until(last.id);
set have;
by id;
do day=admitdate to dischargedate;
substr(days,day-&mind.+1,1)='1';
end;
end;
period=1;
position=1;
do until(position=0);
call scan(days,period,position,length,' ');
if position then do;
AdmitDate = &mind.+position-1;
DischargeDate = AdmitDate + length -1 ;
end;
if position or period=1 then output;
period+1;
end;
drop days day position length ;
run;
Results
Note this could get slow if the range of dates is very long since SAS processes very long character variables slowly. In that case you should probably use an ARRAY instead a character variable. You could use some DO loops to find the periods of flagged days instead of the CALL SCAN method.
... View more