next up previous
Next: %FOR variable in &LIST Up: Loops Previous: Loops

Counters are dates

A useful thing would be to run a macro loop with a changing date. It would be great if I could say

     %macro justaloop;
        %do date ='31Jan1980'd %to '31Jan2000'd;
             <some code>
        %end;
     %mend;
     %justaloop;
   
This is invalid because only integer values can be supplied as index values. SAS thinks that you're trying to loop starting with &date set to be the string '31Jan1980'd.

One option is to get the numeric version of the starting and ending dates and then use those in the loop:

     %let startdate= '31Jan1980'd;
     %let enddate= '31Jan2000'd;
     
     %macro justaloop;
     %let startdate=%sysfunc(putn(&startdate, 8.));
     %let enddate=%sysfunc(putn(&enddate, 8.));
        %do date =&startdate %to &enddate;
             <some code>
        %end;
     %mend;
     %justaloop;
   

The statement

   %let startdate=%sysfunc(putn(&startdate, 8.));

converts the contents of &STARTDATE from '31Jan1980'd to 7335.The %sysfunc macro function allows you to use DATA step functions in macro code. In this case, it allows me to use the PUTN function.

Using a %put statement to tell me how &STARTDATE changes (with options symbolgen on: see Section 9.2):

  %let startdate= '31Jan1980'd;
  %let startdate=%sysfunc(putn(&startdate, 8.));
  %put startdate is &startdate;

the following is written to the log:

              %let startdate= '31Jan1980'd;
        
              %let startdate=%sysfunc(putn(&startdate, 8.));
SYMBOLGEN:  Macro variable STARTDATE resolves to '31Jan1980'd
         
              %put startdate is &startdate;
SYMBOLGEN:  Macro variable STARTDATE resolves to 7335
startdate is 7335

The loop above does whatever some code does for each day between the two dates. However, in this loop I cannot adjust the interval by which the date counter is incremented. For instance, I might want to do something for each end-of-month between startdate and enddate.

To do that, I don't increment the date by 1, instead I increment it manually using a %SYSFUNC call to INTNX:

     %let startdate= '31Jan1980'd;
     %let enddate= '31Jan2000'd;
     
     %macro justaloop;
     %let startdate=%sysfunc(putn(&startdate, 8.));
     %let enddate=%sysfunc(putn(&enddate, 8.));

     %let date=&startdate;
      %do %while(&date <= &enddate);
             <some code>
             %let date=%eval(%sysfunc(intnx(month, &date+1, 1))-1);
        %end;
     %mend;
     %justaloop;
   

Another option is to simply do it by year and month and use the mdy() function to convert it into a date as needed:

     %macro justaloop;
     %do year=1963 %to 2007;
         %do month=1 %to 12;
            
            %let begofmonthdate=%sysfunc(mdy(&month, 1, &year));
            %let endofmonthdate=%eval(%sysfunc(intnx(month, &begofmonthdate, 1))-1);
             <some code>
         %end;
     %end;
     %mend;
     %justaloop;
   

This is not very flexible.


next up previous
Next: %FOR variable in &LIST Up: Loops Previous: Loops
Andre de Souza 2012-11-19