Date difference using sas macro - sas

I have two macro variables value.
a=20150501
b=20160530
I want to create a new macro variable that will display the number of months between a and b.

Same logic as Allan's response but using a data step to simplify the code...
%let a = 20150501;
%let b = 20160530;
data _null_;
a = input(put("&a", 8.), yymmdd8.);
b = input(put("&b", 8.), yymmdd8.);
diff = intck("month", a, b);
call symputx("diff", put(diff, best.));
run;
%put &diff;
12
See documentation for intck for alternative ways to calculate the difference in months.

You can use the INTCK function for that - see documentation
%let a=20150501;
%let b=20160530;
/**
* first - convert to date values
* (DATE_A=01MAY2015 DATE_B=30MAY2016)
*/
%let date_a=%sysfunc(putn(%sysfunc(
INPUTN(%sysfunc(PUTN(&a, Z8.)), YYMMDD8.)
), date9.));
%let date_b=%sysfunc(putn(%sysfunc(
INPUTN(%sysfunc(PUTN(&b, Z8.)), YYMMDD8.)
), date9.));
%put &=date_a &=date_b;
/**
* Now calculate difference using INTCK
* (DIFF=12)
*/
%let diff=%sysfunc(intck(MONTH,"&date_a"d,"&date_b"d));
%put &=diff;

You can use the intck() function to get the number of months difference.
If you are performing a calculation such as age, or tenure, then be sure to use the 'continuous' parameter of intck().
If you simply need to know that there is 1 month difference between the 31-May and the 01-Jun, then use the 'discrete' (default) parameter.
%let a=20150531;
%let b=20150601;
* CONVERT TO SAS DATES;
%let date1 = %sysfunc(inputn(&a,yymmdd8.));
%let date2 = %sysfunc(inputn(&b,yymmdd8.));
* CALCULATE # DISCRETE MONTHS DIFFERENCE;
%let discrete_months = %sysfunc(intck(month, &date1, &date2, d));
%put &=discrete_months;
* CALCULATE # CONTINUOUS MONTHS DIFFERENCE;;
%let cont_months = %sysfunc(intck(month, &date1, &date2, c));
%put &=cont_months;
Output:
DISCRETE_MONTHS=1
CONT_MONTHS=0

Related

Iterate date in loop in SAS

need help on one query , I have to iterate date in do loop that is in format of yymmd6.(202112) so that once the month reach to 12 then its automatically change to next year first month.
///// code////////
%let startmo=202010 ;
%let endmo= 202102;
%macro test;
%do month= &startmo %to &endmo;
Data ABC_&month;
Set test&month;
X=&month ;
%end;
Run;
%mend;
%test;
//////////
Output should be 5 dataset as
ABC_202010
ABC_202011
ABC_202012
ABC_202101
ABC_20210
I need macro variable month to be resolved 202101 once it reached to 202012
Those are not actual DATE values. Just strings that you have imposed your own interpretation on so that they LOOK like dates to you.
Use date values instead and then it is easy to generate strings in the style you need by using a FORMAt.
%macro test(startmo,endmo);
%local offset month month_string;
%do offset = 0 to %sysfunc(intck(month,&startmo,&endmo));
%let month=%sysfunc(intnx(month,&startmo,&offset));
%let month_string=%sysfunc(putn(&month,yymmn6.));
data ABC_&month_string;
set test&month_string;
X=&month ;
format X monyy7.;
run;
%end;
%mend;
%test(startmo='01OCT2020'd , endmo='01FEB2021'd)
And if you need to convert one of those strings into a date value use an INFORMAT.
%let date=%sysfunc(inputn(202010,yymmn6.));
I would prefer to use a do while loop.
check whether the last 2 characters are 12, if so, change the month part to 01.
code
%let startmo=202010 ;
%let endmo= 202102;
%macro test;
%do %while(&startmo <= &endmo);
Data ABC_&startmo;
Set test&startmo;
X=&startmo ;
Run;
%end;
%let mon = %substr(&startmo, 5, 2);
%let yr = %substr(&startmo, 1, 4);
%if &mon = 12 %then %do;
%let m = 01;
%let startmo = %sysfunc(cat(%eval(&yr + 1), &m));
%end;
%else %do;
%let startmo = %eval(&startmo + 1);
%end;
%mend;
%test;

Loop between dates

I created below macro to generate few datasets based on date macro.
%macro pull(date);
proc sql;
create table new&date as
select * from acct
where date=&date.;
quit;
%mend;
So if i want to create dataset for 20170101 20170201 20170301 20170401 20170501, all i can do is use below macro
%macro pull(20170101)
%macro pull(20170201)
%macro pull(20170301)
%macro pull(20170401)
%macro pull(20170501)
What i am planning now is create two macro variables
%let begin=20170101;
%let end =20170501;
and create datasets based on begin and end using loop. Is it possible to do that.So what i am trying to do is give start and end date as macro variable and pull records between begin and end date from acct dataset and create separate datasets for each month between start and end dates
Note dataset have monthly dates for each year.
Below is the code i am trying
%let beg="01jan2000"d;
%let end="01jan2001"d;
%macro Test;
%do date=&beg. %to &end.;
proc sql;
create table IPw_&date. as
select *
from sample
where date=&date. quit;
%end;
%mend;
%Test;
When date information must be inferred from values that are not SAS date values you will need to input the information to get a date value, and put the values iterated over to get the desired non date representation.
This example demonstrates
INPUTN function to parse the YYYYMMDD arguments into date values using informat YYMMDD8.
INTNX function to compute 1st of the month of the date values
PUTN function to convert a date value to a YYYYMMDD representation using format YYMMDDN8.
%DO %WHILE statement for iterating
INTNX function to advance the iteration variable to the start of the next month
Code
%macro pull(yyyymmdd);
%local out;
%let out = pull_&yyyymmdd;
data &out;
pull_date = input ("&yyyymmdd", yymmdd8.);
format pull_date yymmdd10.;
run;
%mend;
%macro pull_each_month(begin=, end=);
%local
begin_date end_date
begin_month end_month
pull_date pull_ymd
;
%put NOTE: &=begin &=end;
%let begin_date = %sysfunc(inputn(&begin,yymmdd8.));
%let end_date = %sysfunc(inputn(&end,yymmdd8.));
%put NOTE: &=begin_date &=end_date;
%let begin_month = %sysfunc(intnx(month,&begin_date,0));
%let end_month = %sysfunc(intnx(month,&end_date,0));
%put NOTE: &=begin_month &=end_month;
%let pull_month = &begin_month;
%do %while (&pull_month <= &end_month);
%let pull_ymd = %sysfunc(putn(&pull_month,yymmddn8.));
%put NOTE: Invoking pull for &=pull_month &=pull_ymd;
%pull (&pull_ymd)
%let pull_month = %sysfunc(INTNX(MONTH,&pull_month,1));
%end;
%mend;
%pull_each_month (
begin = 20170101
, end = 20170501
)
%macro pull_each_month(begin=, end=);
%local
begin_date end_date
begin_month end_month
pull_date pull_ymd
;
%put NOTE: &=begin &=end;
%let begin_date = %sysfunc(inputn(&begin,yymmdd8.));
%let end_date = %sysfunc(inputn(&end,yymmdd8.));
%put NOTE: &=begin_date &=end_date;
%let begin_month = %sysfunc(intnx(month,&begin_date,0));
%let end_month = %sysfunc(intnx(month,&end_date,0));
%put NOTE: &=begin_month &=end_month;
%let pull_month = &begin_month;
%do %while (&pull_month <= &end_month);
%let pull_ymd = %sysfunc(putn(&pull_month,yymmddn8.));
%put NOTE: Invoking pull for &=pull_month &=pull_ymd;
%let pull_month = %sysfunc(INTNX(MONTH,&pull_month,1));
%end;
%mend;
%pull_each_month (
begin = 20170101
, end = 20170501
)
%macro pull(begin,end);
%let i=0;
%let begin=%sysfunc(inputn(&begin,anydtdte9.));
%let end=%sysfunc(inputn(&end,anydtdte9.));
%do %until (&begin=&end);
%let begin=%sysfunc(intnx(month,&begin,&i));
%let date=%sysfunc(putn(&begin,yymmddn8.));
proc sql;
create table new&date as
select * from acct where date=&date.;
quit;
%let i=%eval(&i+1);
%end;
%mend;
%pull(20170101,20170501)

Prompt or Macro Variables used in calculations

I have created a numeric variable using the Prompt Manager in EG.
This variable is called HYr for the highest year of data that I am pulling.
When running the program I create 4 new variables based on the highest year and this is where I am having issues.
I have the following:
%Let Yr2 = &HYr. - 1;
%Let Yr3 = "&HYr." - 2;
%Let Yr4 = &HYr. - 3;
%Let Yr5 = '&HYr.' - 4;
I am trying to subtract the value from the year and the new variable will be used in determining date ranges that are being pulled. I am trying several things and learning in the process but I am still stuck.
I know it is probably just a simple syntax issue and given enough time I will probably be able to get it but no one in my office has any better SAS skills than I do and that isn't much.
thanks in advance.
Use %EVAL() to do calculations with integers and macro variables.
%let HYR = 2018;
%Let Yr2 = %eval(&HYr. - 1);
%Let Yr5 = %eval(&HYr. - 4);
%put HYR: &hyr;
%put YR2: &yr2.;
%put YR5: &yr5.;
EDIT: If you were trying to do other calculations that included decimals you would need to use %SYSEVALF instead.
%let HYR = 2018;
%Let Yr2 = %sysevalf(&HYr. - 0.1);
%Let Yr5 = %sysevalf(&HYr. - 0.4);
%put HYR: &hyr;
%put YR2: &yr2.;
%put YR5: &yr5.;

passing date variable to macro for sysfunc processing

below is my little problem to create a macro and passing in a date variable. Without using the date variable, it works with results as below.
%macro x();
%let i=-1;
%let dts = %sysfunc(today());
%put &dts; /*ok*/
%let yymm1 = %sysfunc(intnx(MONTH,&dts,&i));
%put &yymm1; /*ok*/
%let mth_beg = %sysfunc(intnx(MONTH,&dts,&i,B),date9.);
%let mth_end = %sysfunc(intnx(MONTH,&dts,&i,E),date9.);
%put &mth_beg &mth_end; /*01JAN2018 31JAN2018*/
/*** proc sql code below ** */
%mend;
%x();
log:
21231
21185
01JAN2018
31JAN2018
Now I create a macro around it and got the following error:
%macro x(dts1);
%let i=-1;
/*%let dts = %sysfunc(today());*/
%let dts = %sysfunc(&dts1);
%put &dts; /*ok*/
%let yymm1 = %sysfunc(intnx(MONTH,&dts,&i));
%put &yymm1; /*ok*/
%let mth_beg = %sysfunc(intnx(MONTH,&dts,&i,B),date9.);
%let mth_end = %sysfunc(intnx(MONTH,&dts,&i,E),date9.);
%put &mth_beg &mth_end; /*01JAN2018 31JAN2018*/
/*** proc sql code below ** */
%mend;
%x(16JAN2018);
ERROR: Function name missing in %SYSFUNC or %QSYSFUNC macro function reference.
JAN2018)
ERROR: Expected close parenthesis after macro function invocation not found.
))
ERROR: Expected close parenthesis after macro function invocation not found.
ERROR: Expected close parenthesis after macro function invocation not found.
,B),date9.) ,E),date9.)
I am not sure how to let SAS treat the date passed in as a recognized date. I know i probably used the sysfunc(&dts) wrongly or the date passed in need to adhere to certain format. i just want the date to replace today(). Can you help? I am a SAS newbie.
thanks
Wrap the date in " and end with a d. That will tell SAS to convert the string to a date:
%macro x(dts1);
%let i=-1;
/*%let dts = %sysfunc(today());*/
%let dts = "&dts1"d; /*Change here!*/
%put &dts; /*ok*/
%let yymm1 = %sysfunc(intnx(MONTH,&dts,&i));
%put &yymm1; /*ok*/
%let mth_beg = %sysfunc(intnx(MONTH,&dts,&i,B),date9.);
%let mth_end = %sysfunc(intnx(MONTH,&dts,&i,E),date9.);
%put &mth_beg &mth_end; /*01JAN2018 31JAN2018*/
/*** proc sql code below ** */
%mend;
%x(16JAN2018);
change %let dts = %sysfunc(&dts1); to
%let dts = %sysfunc(inputn(&dts1,date9. ));
SAS stores dates as the number of days since 01JAN1960. So if you do not attach a date format to the date value it will just look like an integer.
%let today=%sysfunc(today());
You can then use that integer anywhere you would use a date value.
%let next_month=%sysfunc(intnx(month,&today,1,b));
You can also represent dates by using a date literal. To make a date literal you represent the date value using something the DATE informat can read (like 16FEB2018, 16feb18, 16-FEB-2018, etc.) enclosed in quotes with the letter d appended.
%let today="%sysfunc(today(),date9)"d ;
%let date_string=13FEB2018;
%let date_value="&date_string"d ;
So date literals will work in SAS code and when you use the %sysfunc() macro function to call a SAS function (like INTNX) and they will work in the %sysevalf() macro function. But the %eval() macro function will not recognize date literals. So you will need to use %sysevalf() if you want use arithmetic or comparisons of date literals in macro logic.
%if %sysevalf(&today > '01JAN2018'd) %then ....
%let tomorrow=%sysevalf(&today +1);

SAS - conditional macro variable

I have a variable date=201611 and I need to create the first day of the next month in the following format '2016-12-01'. The following code works fine for the months up till 11:
%let date = 201611;
%let rok = %sysfunc(substr(&date,1,4));
%let month = %sysfunc(substr(&date,5,2));
%let xdat2_ii = &rok-%eval(&month + 1)-01;
%let xdat1 = %str(%')&xdat2_ii.%str(%');
%put &xdat1;
'2016-12-01'
I need to add some improvement to make the code working for the month 12, i.e. when the date is 201612 then to obtain '2017-01-01'.
My idea was to do it using macro, but it does not work.
%macro promenne;
%if &month < 12 %then %let xdat2_ii = &rok-%eval(&month + 1)-01
%else %if &month= 12 %then %let xdat2_ii = %eval(&rok + 1)-01-01;
%mend promenne;
Thank you for any suggestions which way to go.
When working with dates, is often easiest to use the built in date shifting functions - in this case, intnx.
/* define variable (this is a macro STRING) */
%let date=201612;
/* convert to SAS date value (numeric, num of days since 01JAN1960) */
%let dateval=%sysfunc(mdy(%substr(&date,5,2),1,%substr(&date,1,4)));
/* finally - shift to beginning of following month and format output */
%let xdat2_ii=%sysfunc(intnx(MONTH,&dateval,1,B),yymmddd10.);
%put &xdat2_ii; /* 2017-01-01 */