Relatively new to SAS and looking for some help with using date functions.
I'm using Today()-x to set dates and making it available for looking at historic data (a rare need, but worth including) and have tried writing an IF statement around it, as I need a slight change for financial year end.
My code is as follows:
%macro RunDate_Variables;
%global rep_month repdt prevdt;
data _null_;
rundate = today()-0;
runmonth = Month(rundate);
%if runmonth eq 04
%then %do;
ReportDate = MDY(4, 4, Year(rundate);
PrevReportDate = intnx('Month', ReportDate,-2,'e');
%end;
%else %if runmonth eq 05;
%then %do;
ReportDate = intnx('Month', rundate,-1,'e');
PrevReportDate = iMDY(4, 4, Year(rundate));
%end;
%else %do;
ReportDate = intnx('Month', rundate,-1,'e');
ReportDate = intnx('Month', ReportDate,-1,'e');
%end;
call symputx('rep_month', put(ReportDate, MONYY7.));
call symputx('repdt', put(ReportDate, yymmddN8.));
call symputx('prevdt', put(PrevReportDate, yymmddn8.));
run;
%mend;
%RunDate_Variables;
Unfortunately, the runmonth doesn't seem to be doing what I'd hoped it would do and therefore isn't resolving as expected within the macro.
Ultimately, what I'm looking for, is for the code to identify when the code is being run in April and set the ReportingDate to 4th April (for Financial Year End) and PrevReportingDate to 28th (or 29th) Feb, and for when the code is run in May for the ReportingDate to be set as 30th April and PrevReportingDate to be set as 4th April.
The code won't need to be run in the space between 31st March and 4th April, so having it resolve to 4th April in that space won't be an issue.
Any suggestions appreciated. The code doesn't need to stick with the format I've attempted, the only bit that needs to remain the same are the variable names rep_month, repdt and prevdt as these were the names used throughout the code when I picked the project up.
EDIT: I've managed to make the idea work using a manually adjusted flag outside the data step and altering the IF conditions, but the issue for this is that it requires either two flags (one for April and one for May), or two separate inputs to be read from the Flag (again, one to recognise April, the other May). Whilst this works, I'd still like to look into the possibility of fully automating this, as FYE is an easy time to remember to alter the flag, but altering the flag in the month after FYE will likely be less memorable.
Thanks.
You cannot check the values of data variables with macro code, your %IF statement is just comparing the constant string runmonth to the constant string 04 . They will never be equal.
I don't see anything here that requires any macro logic, but you could still keep the macro definition if you want to call the same data step multiple times.
data _null_;
rundate = today()-0;
runmonth = Month(rundate);
if runmonth eq 04 then do;
ReportDate = MDY(4, 4, Year(rundate);
PrevReportDate = intnx('Month', ReportDate,-2,'e');
end;
else if runmonth eq 05 then do;
ReportDate = intnx('Month', rundate,-1,'e');
PrevReportDate = MDY(4, 4, Year(rundate));
end;
else do;
ReportDate = intnx('Month', rundate,-1,'e');
PrevReportDate = intnx('Month', ReportDate,-1,'e');
end;
call symputx('rep_month', put(ReportDate, MONYY7.),'g');
call symputx('repdt', put(ReportDate, yymmddN8.),'g');
call symputx('prevdt', put(PrevReportDate, yymmddn8.),'g');
run;
Related
I am writing a conditional in SAS starts with DATA NULL
%LET today = today();
DATA _NULL_;
if day(today) ge 1 and day(today) le 15 then do;
date1=put(intnx('month',today,-1,'E'), date11.);
date2=put(intnx('month',today,-1,'L'), date11.);
end;
if day(today) > 15 then do;
date1=put(intnx('month',today,0,'B'), date11.);
date2=put(intnx('month',today,0,'L'), date11.);
end;
call symput('report_date',date1);
call symput('report_date2',date2);
RUN;
but with above, I am not getting any values for my report_dates.
the condition is:
date 1 = If the current date is greater than or equal to 1 and less than 16, set the date1 to the 16th of the previous month, otherwise set it to the 1st of the current month
date2 = If the current date is 16 and above, set the date2 to the 15th of the current month, otherwise set date2 to the last day of the previous month
The IF/THEN logic does not account for the missing value you passing to the DAY() function calls.
The variable TODAY is never created in the data step. So just remove the %LET statement and add an actual assignment statement instead.
DATA _NULL_;
today=today();
if day(today) ge 1 and day(today) le 15 then do;
...
Just because you used the same name for the macro variable in the %LET statement as you used for the variable in the data step does not imply that the two have anything at all to do with each other.
If you wanted to use the macro variable to generate the code for the data step you would need to replace the TODAY with &TODAY.
if day(&today) ge 1 and day(&today) le 15 then do;
So for the value you set to the macro variable TODAY it would mean that the SAS code you are trying to run is:
if day(today()) ge 1 and day(today()) le 15 then do;
Note that is actually not a good way to handle this problem because it is calling the TODAY() function multiple times. That could cause strange results if the data step started right before midnight. So the IF condition might be run on the 31st but when you get to the ELSE condition the clock has ticked over to the first of the next month.
I have a set of code that I am manually adjusting month end dates. The query which runs and uses the dates has left joins to pull data for each declared date. I've been looking at macros because this is labor intensive to change all the dates by one month all the way until the most recent completed month. There's got to be a way to loop through and change each declared month end date by one month and do a loop to run the query until it gets to the most recent completed month end date.
So after the below runs entirely, it would up each declared date by one month and show the month end date. Each data set that runs would be be BEDT. So the first run would be jan2018, so on and so forth.
Can someone assist or point me into the right direction? I've read through so much documentation and have gotten no where. Thanks in advance.
%Let BEdt='01JAN2018'd
%let dt1='28FEB2018'
%let dt2='31MAR2018'
%let dt3='30APR2018'
%let dt4='31MAY2018'
%let dt5='30JUN2018'
%let dt6='31JUL2018'
%let dt7='31AUG2018'
%let dt8='30SEP2018'
%let dt9='31OCT2018'
%let dt10='30NOV2018'
%let dt12='31DEC2018'
%let dt12='31JAN2019'
Let's use something that has a start date and a number of months to pull. We'll use intnx() to increment each month one by one until we reach the last month. intnx() will also help us calculate the start and end dates.
%macro get_data(start=, months=12);
/* Convert start/end values into SAS dates */
%let start_dt = %sysfunc(inputn(&start., date9.));
%do i = 0 %to &months.;
/* Calculate the start and end months for the month of data in the loop */
%let month_s = %sysfunc(intnx(month, &start_dt., &i., B) );
%let month_e = %sysfunc(intnx(month, &start_dt., &i., E) );
%put Pulling data for %cmpres(%sysfunc(putn(&month_s., date9.)) - %sysfunc(putn(&month_e., date9.)));
/***********************************/
/***** SQL/DATA Step goes here *****/
/***********************************/
%end;
%mend;
%get_data(start=01JAN2018);
In the area where you'd add SQL or DATA step code, it could look like this:
data want_&month_s.;
set have;
where date BETWEEN &month_s. AND &month_e.;
run;
Output:
Pulling data for 01JAN2018 - 31JAN2018
Pulling data for 01FEB2018 - 28FEB2018
Pulling data for 01MAR2018 - 31MAR2018
Pulling data for 01APR2018 - 30APR2018
Pulling data for 01MAY2018 - 31MAY2018
Pulling data for 01JUN2018 - 30JUN2018
Pulling data for 01JUL2018 - 31JUL2018
Pulling data for 01AUG2018 - 31AUG2018
Pulling data for 01SEP2018 - 30SEP2018
Pulling data for 01OCT2018 - 31OCT2018
Pulling data for 01NOV2018 - 30NOV2018
Pulling data for 01DEC2018 - 31DEC2018
Pulling data for 01JAN2019 - 31JAN2019
Depending on the complexity of the statement you want to run for each month, you can also use call execute instead of a macro loop.
data _null_;
start_date = "&start."d;
do i = 0 to &months.;
month_start = intnx("month", start_date, i, "B");
month_end = intnx("month", start_date, i, "E");
call execute(
'data want_'||strip(i)||';'||
'set have;'||
'where date between '||month_start||' and '||month_end||';'||
'run;'
);
end;
run;
You only have to be careful about when macro variables get resolved. I nice article on this can be found here
* Do whatever you would do for one month in a macro;
%macro do_a_month(begin_d, end_d);
data want_&begin_d;
set have;
where data between "&begin_d"d and "&end_d"d;
run;
%mend;
* call the macro as much as needed;
data _null_;
begin_d = '01JAN2018'd;
do while (begin_d le '31JAN2019'd);
end_d = intnx("month", begin_d, 0, "E");
call execute ( '&do_a_month('
|| put(begin_d, date9.) ||','
|| put(end_d, date9.) ||')';
* go to next month;
begin_d = end_d + 1;
end;
run;
Suppose that I have a library save which contains daily files thefile_dly_yyyymmdd,
e.g. save.thefile_dly_20150831, save.thefile_dly_20150901, ... , save.thefile_dly_20210731.
I want to perform some manipulation on the historical data in this library, however, I only want to extract the files between a specific date, and also only want to keep the file which corresponds to the last file for each month, e.g. I want to extract save.thefile_dly_20150831, save.thefile_dly_20150930, save.thefile_dly_20151031, etc.
Something like the following.
%macro loop_through(start,end);
%do i = &start. %to &end.;
%if %sysunc(exist(SAVE.THEFILE_DLY_&i.)) %then %do;
/* Do some data processing on the file */
%end;
%end;
%mend;
%loop_though(20150831,20210731);
The problem is that the abovementioned code will loop through every single integer between 20150831 and 20210731, which is not optimal, and also, it will process every single file that exists for the month, and not just the file corresponding to the last day of each month.
How can I adjust? Any advice will be appreciated.
To loop over calendar intervals iterate over the number of intervals. Use the INTNX() function to calculate the next interval's date. Use the INTCK() function to calculate the number of intervals requested.
%macro loop_through(start,end);
%local offset ymd dsname ;
%do offset = 0 %to %sysfunc(intck(month,&start,&end));
%let ymd=%sysfunc(intnx(month,&start,&offset,end),yymmddn8.);
%let dsname=SAVE.THEFILE_DLY_&ymd;
%if %sysunc(exist(&dsname)) %then %do;
/* Do some data processing on the file */
%end;
%end;
%mend;
%loop_though('01AUG2015'd,'01JUL2021'd);
If you really want to allow the user of the macro to pass in YYYYMMDD digit strings instead of actual SAS date values then add some logic to your macro to convert the digit strings into actual date values. For example:
%let start=%sysfunc(inputn(&start,yymmdd8.));
I am setting up a batch script in SAS, I want it to run monthly and output to table, the code works when running manually but I am having difficulty setting up the macros.
I have created 3 macros, one for the previous month (please ignore the hard coded 12 and the commented out code, I was just testing if it worked or not, the commented out code will remain and the hard coded 12 will be removed when I can get it working), one for the current year and one for the previous year. The issue I will have is that the script grabs month end data, so come January the year will be set to 2020 but the month will be looking for December data.
`%let pmonth = 12 /*%sysfunc(month(%sysfunc(intnx(month,"&sysdate"d ,-1))))*/;`
`%put &pmonth;`
`%let year1 = %sysfunc(year("&sysdate"d));`
`%put &year1;`
`%let year2 = %sysfunc(year(%sysfunc(intnx(year,"&sysdate"d ,-1))));`
`%put &year2;`
`%macro year;
%if &pmonth = 12 %then %do;
&year2;
%end;
%else %do;
&year1;
%end;
%mend;
%year;`
What I would like is for the macro year, which is used in my code, to select correctly between year1 and year2 based on the month macro pmonth. I have played around a bit with the macros but if I'm honest, its not an area of SAS I've used that often.
A little terminology first. Your code has one macro defined, %YEAR. It does have three macro variables named YEAR1, YEAR2 and PMONTH. And there is no macro variable named YEAR that your code is either attempting to create or use. And your code does not have any IF statements. But there is one %IF statement inside the definition of the YEAR macro.
You probably will want to convert your macro variable values into actual DATE values to deal with the year issue. So if your input is two macro variables with YEAR and MONTH you can convert that into a date.
%let year1=2018;
%let month=1;
%let date=%sysfunc(mdy(&month,1,&year1));
Then if you want to find the end of the previous month you can use the date in the INTNX() function call.
%let end_of_prev_month=%sysfunc(intnx(month,&date,-1,e));
Then if you need to you can get the YEAR and MONTH for that new date.
%let year2=%sysfunc(year(&end_of_prev_month));
%let month2=%sysfunc(month(&end_of_prev_month));
I am an intermediate user of SAS, but I have limited knowledge of arrays and macros. I have a set of code that prompts the user to enter a date range. For example, the user might enter December 1, 2015-December 5,2015. For simplicity, imagine the code looks like:
data new; set old;
if x1='December 1, 2015'd then y="TRUE";
run;
I need to run this same code for every day in the date prompt range, so for the 1st, 2nd, 3rd, 4th, and 5th. My thought was to create an array that contains the dates, but I am not sure how I would do that. My second thought was to create a macro, but I can't figure out out to feed a list through a macro.
Also, just FYI, the code is a lot longer and more complicated than just a data step.
The following macro can be used as a framework for your code:
%MACRO test(startDate, endDAte);
%DO i=&startDate %to &endate;
/* data steps go here */
/* example */
DATA test;
SET table;
IF x1 = &i THEN y = "true";
RUN;
%END;
%MEND;
Look into call execute to call your macro and a data null step using a do loop to loop through the days. Getting the string correct for the call execute can sometimes be tricky, but worth the effort overall.
data sample;
do date='01Jan2014'd to '31Jan2014'd;
output;
end;
run;
%macro print_date(date);
proc print data=sample;
where date="&date"d;
format date date9.;
run;
%mend;
%let date_start=05Jan2014;
%let date_end=11Jan2014;
data _null_;
do date1="&date_start"d to "&date_end"d by 1;
str='%print_date('||put(date1, date9.)||');';
call execute(str);
end;
run;