BookmarkSubscribeRSS Feed
soujik
Calcite | Level 5

%macro dt_date(date=,interval=,format=,offset=-1,alignment=B,quote=Y)/minoperator;
%put Start macro dt_date(date=&date,interval=&interval,format=&format,offset=&offset,alignment=&alignment,quote=&quote);

%local interval_temp;

%local d fmt rc dsid;

%if %superq(date)=%str() %then %let date=&dt_sas;

%let interval=%upcase(&interval);

%let quote=%upcase(&quote);

%let alignment=%upcase(&alignment);

%if &format ne %str() %then
%do;

%let fmt=%upcase(%sysfunc(compress(&format,%str(.),d)));

%let dsid=%sysfunc(open(sashelp.vformat(where=(fmtname="&fmt"))));

%let rc=%sysfunc(fetch(&dsid));

%let dsid=%sysfunc(close(&dsid));

%if &rc=-1 %then
%do;

%let errormsg1=&format is not a valid format.;

%let jumptoexit=1;

%let d=;

%goto EXIT;

%end;

%end;
%else
%do;

%let format=best.;

%let quote=N;

%end;

%let interval_temp=%scan(&interval,1,%str(.));

%let pos=%sysfunc(anydigit(&interval_temp));

%if &pos %then %let interval_temp=%substr(&interval_temp,1,%eval(&pos-1));

%if %eval(&interval_temp in YEAR QTR MONTH WEEK DAY YEARLY QUARTERLY MONTHLY WEEKLY DAILY)=0 %then
%do;

%let errormsg1=&interval is not a valid date interval.;

%let jumptoexit=1;

%let d=;

%goto EXIT;

%end;

%if %sysfunc(inputn(&offset, best.))=%str() %then
%do;

%let errormsg1=&offset is not a valid offset.;

%let jumptoexit=1;

%let d=;

%goto EXIT;

%end;

%if &quote ne Y and &quote ne N %then
%do;

%let errormsg1=&quote is not a valid Quote value. Must be Y or N.;

%let jumptoexit=1;

%let d=;

%goto EXIT;

%end;

%if &alignment ne B and &alignment ne E and &alignment ne M and &alignment ne S %then
%do;

%let errormsg1=&alignment is not a valid alignment value. Must be B, E, M, S.;

%let jumptoexit=1;

%let d=;

%goto EXIT;

%end;

%let date=%sysfunc(intnx(&interval,&date,&offset,&alignment));

%let d=%sysfunc(putn(&date,&format));

%if %superq(d)=%str() %then
%do;

%let errormsg1=&format is not a valid format.;

%let jumptoexit=1;

%let d=;

%goto EXIT;

%end;


%if &quote=Y %then %let d=%unquote(%str(%')&d%str(%'));

%EXIT:

%unquote(&d)

%put End macro dt_date - Date Value returned is %unquote(&d);
%mend dt_date;

6 REPLIES 6
Patrick
Opal | Level 21

This code demonstrates why developers should spend the time to add inline comments with explanations. 

I believe a "explain everything" is likely a too big ask for this forum. 

 

I just tried and asked ChatGPT to explain the code. The answer wasn't too bad. I suggest you try this first and then come back here with some targeted detail questions should there still be a need. ....and of course if there are just some macro functions you don't understand first consult the SAS docu.

SASKiwi
PROC Star

Adding to @Patrick 's comment about lack of comments, there is a complete lack of proper code layout to make it at least readable. That includes having blank lines between sections and indenting %DO blocks.

WarrenKuhfeld
Rhodochrosite | Level 12

Adding to the other comments. Don't use the macro language when you can use ordinary SAS code. I wrote some multi-thousand line macros in my day. Of course they had macro code. That said, when ever possible, I used DATA _NULL_ DATA step code to process parameters and strings. It makes your life much easier.

Tom
Super User Tom
Super User

It is an over complicated way to call the INTNX() function.

%sysfunc(intnx(&interval,&date,&offset,&alignment),&format)

If you are interested here is a partial list of the logic/coding mistakes:

Spoiler
Adds the / MINOPERATOR option to the %MACRO statement and then does not use IN as an operator (even in places where it would make sense in this program).

Use of "magic" macro variables.  Referencing a macro variable in the middle of the macro that not an input and has no comment or other code explaining where its value should come from.
%if %superq(date)=%str() %then %let date=&dt_sas;

Assumes that just removing the period from a format specification will result in the bare format name.
  %let fmt=%upcase(%sysfunc(compress(&format,%str(.),d)));

Using BEST as the name of an INFORMAT 
%if %sysfunc(inputn(&offset, best.))=%str() %then %do;
Assuming that PUTN() will return an empty string when the value is missing instead of the normal single period.
%let d=%sysfunc(putn(&date,&format));
%if %superq(d)=%str() %then %do;

Unquoting a value that was never quoted.
%unquote(&d)

And for something that is going to a lot (too much really) trouble to validate its input it will stop the macro when the first invalid input is detected.  So if a user has made mistakes on multiple inputs they will have to make many calls to discover all of the parameter errors they have made.











 

Tom
Super User Tom
Super User

Note it does not handle this format:

461  %put %dt_date(date='01JAN2023'd,interval=month,format=e8601da.,offset=-1,alignment=B,quote=Y);
Start macro dt_date(date='01JAN2023'd,interval=month,format=e8601da.,offset=-1,alignment=B,quote=Y)
End macro dt_date - Date Value returned is

462  %put %sysfunc(intnx(month,'01JAN2023'd,-1,b),e8601da.);
2022-12-01
s_lassen
Meteorite | Level 14

I think I found out why - another error in the macro:

%let fmt=%upcase(%sysfunc(compress(&format,%str(.),d)));

This removes all digits from the format name, including the "8601" in "E8601DA.", so the format name becomes "EDA". The code does not flag this error though, as the  code

%let errormsg1=&format is not a valid format.;

does not  write the error message anywhere (and the variable is %LOCAL, unless it has been defined outside the macro).

 

For those who want to see what the macro does, here is a version in readable format:

%macro dt_date(date=,interval=,format=,offset=-1,alignment=B,quote=Y)/minoperator;
  %put Start macro dt_date(date=&date,interval=&interval,format=&format,offset=&offset,alignment=&alignment,quote=&quote);
  %local interval_temp;
  %local d fmt rc dsid;

  %if %superq(date)=%str() %then %let date=&dt_sas;
  
  %let interval=%upcase(&interval);
  %let quote=%upcase(&quote);
  %let alignment=%upcase(&alignment);
  
  %if &format ne %str() %then %do;
    %let fmt=%upcase(%sysfunc(compress(&format,%str(.),d)));
    %let dsid=%sysfunc(open(sashelp.vformat(where=(fmtname="&fmt"))));
    %let rc=%sysfunc(fetch(&dsid));
    %let dsid=%sysfunc(close(&dsid));
    %if &rc=-1 %then %do;
      %let errormsg1=&format is not a valid format.;
      %let jumptoexit=1;
      %let d=;
      %goto EXIT;
      %end;
    %end;
  %else %do;
    %let format=best.;
    %let quote=N;
    %end;

  %let interval_temp=%scan(&interval,1,%str(.));
  %let pos=%sysfunc(anydigit(&interval_temp));
  %if &pos %then %let interval_temp=%substr(&interval_temp,1,%eval(&pos-1));
  
  %if %eval(&interval_temp in YEAR QTR MONTH WEEK DAY YEARLY QUARTERLY MONTHLY WEEKLY DAILY)=0 %then %do;
    %let errormsg1=&interval is not a valid date interval.;
    %let jumptoexit=1;
    %let d=;
    %goto EXIT;
    %end;

  %if %sysfunc(inputn(&offset, best.))=%str() %then %do;
    %let errormsg1=&offset is not a valid offset.;
    %let jumptoexit=1;
    %let d=;
    %goto EXIT;
    %end;

  %if &quote ne Y and &quote ne N %then %do;
    %let errormsg1=&quote is not a valid Quote value. Must be Y or N.;
    %let jumptoexit=1;
    %let d=;
    %goto EXIT;
    %end;

  %if &alignment ne B and &alignment ne E and &alignment ne M and &alignment ne S %then %do;
    %let errormsg1=&alignment is not a valid alignment value. Must be B, E, M, S.;
    %let jumptoexit=1;
    %let d=;
    %goto EXIT;
    %end;

  %let date=%sysfunc(intnx(&interval,&date,&offset,&alignment));
  %let d=%sysfunc(putn(&date,&format));
  %if %superq(d)=%str() %then %do;
    %let errormsg1=&format is not a valid format.;
    %let jumptoexit=1;
    %let d=;
    %goto EXIT;
    %end;


  %if &quote=Y %then %let d=%unquote(%str(%')&d%str(%'));

  %EXIT:
%unquote(&d)

%put End macro dt_date - Date Value returned is %unquote(&d);
%mend dt_date;

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 6 replies
  • 771 views
  • 6 likes
  • 6 in conversation