Automating the starting date with a macro variable in SAS - sas

Background:
I have a code that pulls transactional data starting at the beginning of the current calendar quarter, but from an year ago.
For example, if I run the code today (August 16, 2013) it will have to pull all the data from July 1, 2012 onwards.
Problem:
I want to automate the starting date for the data pull with a macro variable.
So far, I'm stuck here:
%let ThisYear = %Sysfunc(Date(), YEAR.);
%let LastYear= %eval(&ThisYear-1); /* I get the starting year */
%let QTR_start_month= %eval(3*%Sysfunc(Date(), qtr.)-2); /* this gives me the current quarter starting month. If I run it in August, it outputs 7 for July */
%let start_date=%str(01/%Sysfunc(month(&QTR_start_month))/&lcy);
The final macro variable outputs the date which I want, but in a format which is not recognized by SAS.
I will greatly appreciate any help.
Many thanks in advance!

You can either input that date to a date format, or construct it like a SAS date literal ('01JUL2013'), DDMONYY(YY), or construct it as a date value directly.
INTNX is probably your best option here to construct it; you don't need all that work.
%let start_date = %sysfunc(intnx(Quarter,%sysfunc(date()),-4),DATE9.);
%put &start_date;
You can leave DATE9. to use it as a date literal, or remove the ,DATE9. to get the numeric value that can be used directly. You would use this as "&start_Date."d to use the date literal.

This should do the job.
data test;
format todays_date starting_qtr date9.;
todays_date=today();
/*this takes today's date and rolls back 4 qtrs and sets that date to the first day of that quarter*/
starting_qtr = intnx('qtr',todays_date,-4,'b');
/*so, running this code today, 16AUG2013 would yield starting_qtr=01JUL2012 */
call symputx('start_date', put(starting_qtr, date9.));
run;
%put &start_date.;

Related

SAS macro to get the last date of the current month

I would like to use a macro in SAS to calculate the last day of the current month when executed.
As i'm quite new to the SAS macro's i've tried to create on based on the information i've found on the internet.
%let last_day = %sysfunc(putn(%sysfunc(intnx(month,%sysfunc(today()),e), date9.));
However it does not seem to work when i execute it.
You left out the number of intervals in the INTNX() function call.
To create a macro variable with the string that looks like the last day of the current month in the style produced by the DATE9. format just use:
%let last_day = %sysfunc(intnx(month,%sysfunc(today()),0,e), date9.);
You could then use that macro variable to generate strings. Such as in a TITLE statement.
TITLE "End of the month is &last_day";
If you want to use it as an actual date you will need to convert it to a date literal by adding quotes and the letter d.
...
where date <= "&last_day"d ;
And if so it is probably simpler to not use the DATE9. format at all and just store the raw number of days since 1960 in the macro variable.
%let last_day = %sysfunc(intnx(month,%sysfunc(today()),0,e));
...
where date <= &last_day ;

SAS HANA Date from data step in PRO

A little new to SAS here. I am using the following data step to get the first and last day of the month.
Data _NULL_;
begindt=IntNX("Month", Date(), 0) ;
enddt=IntNX("Month", Date(),0,'E');
PUT begindt=E8601DA. enddt=E8601DA.;
Run;
The data step gets the results of begindt=2021-09-01 and enddt=2021-09-30.
However, I am having trouble converting the value to a date format to use in a where claus in a PROC SQL statement later in the program. The commented out code works, but I can't get the date from data step in the correct format for the PROC SQL statement to work.
/* AND "DETAILAR"."CLEAR_DOC_POSTING_DATE" = '2021-09-01' */
AND "DETAILAR"."CLEAR_DOC_POSTING_DATE" = begindt
SAS has date and time literals that make dealing with dates and times easy. SAS dates are the number of days since Jan 1 1960, and SAS datetimes are the number of seconds since Jan 1 1960. SAS automatically converts date literals to these times for you. Some examples of date and datetime literals:
'04SEP2021'd
'04SEP2021:00:00'dt
You don't have to use these all the time*, but they make debugging way easier. In your case, you simply need to feed a date literal into proc sql. If you're connecting to SAP HANA through SAS, the SAS/ACCESS engine to SAP will handle the conversion for you.
data _null_
begindt = intnx('month', today(), 0, 'B');
enddt = intnx('month', today(), 0, 'E');
call symputx('begindt', put(begindt, date9.) );
call symputx('enddt', put(enddt, date9.) );
run;
Or, equivalently:
%let begindt = %sysfunc(intnx(month, %sysfunc(today()), 0, B), date9.);
%let enddt = %sysfunc(intnx(month, %sysfunc(today()), 0, E), date9.);
Now you have two macro variables that you can not only easily read, but SAS will convert them for you. You can view them below:
%put &begindt;
%put &enddt;
Simply add them as date literals to your where clause in proc sql and let SAS do the rest.
proc sql;
create table want as
select *
from have
where CLEAR_DOC_POSTING_DATE BETWEEN "&begindt"d AND "&enddate"d
;
quit;
There are other literals too, like time literals, hex literals and name literals for variables with spaces in them.
'10:00't - Time Literal
'32'x - Hex literal
'this is a var'n - Name literal
*proc timeseries, proc timedata, and proc tsmodel require date/datetime literals for the start and end options. But those are the only ones I know of.
If you want to generate code like '2021-09-01' then why not create a macro variable with that string in it?
In your data _null_ step use:
call symputx('begindt',quote(put(intnx('month',date(),0),yymmdd10.),"'"));
Working from the inside out that statement will:
calculate today's date
convert to the start of the month
convert to a 10 character string representing that date
add single quotes around the string
store the value into a macro variable named begindt
Now you reference the macro variable to generate the code you want
and "DETAILAR"."CLEAR_DOC_POSTING_DATE" = &begindt.
Which will generate the code:
and "DETAILAR"."CLEAR_DOC_POSTING_DATE" = '2021-09-01'

I am having difficulty using an IF statement within a SAS Macro

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));

Counting working days in SAS EG

Hi to all and good time of a day!
Here is my case I need to solve I will very gratefull if you can help me.
I have some data set it contains only one variable date format.
Example:
01JAN2016
06JAN2016
15FEB2016
The second data set is days - holidays for a period 5 years.
Example:
01JAN2016
02JAN2016
and etc, all these days are not working days.
The case is I need to count number of working days from date for every observation from first data set till now. It seems that I need to count number of days
"Now date" minus Date(from first data set) and minus number of days from second data set with holidays (count(date) where Date(from first data set)< date < "Now"
You can define your own type of interval to use with SAS funcions intck and intnx. Here's how to do it:
First create a table of weekdays for whichever years you have holidays for, up to present (or a future) year.
Here we'll start by including all weekdays from 2014 to 2016. This is assuming you don't want to count weekend days. If that's not the case, just modify the code so that the condition "weekday(date) in (2:6)" is not applied. You'll get the full 365 days of the year.
data mon_fri;
do date = "01JAN2014"d to "31DEC2016"d;
if weekday(date) in (2:6) then output;
end;
format date date9.;
run;
Then we'll create a table having all those dates we just created, minus the holidays we have in the table Holidays. We'll place the table in a library called myLib, and rename the date column to "Begin" for compliance with SAS custom intervals.
libname myLib "some/place/on/your/drive";
data mylib.workdays(RENAME=(date=Begin));
merge mon_fri (in=weekday)
Holidays (in=holiday);
by date;
if weekday and not holiday then output;
run;
Now we set up a custom interval which we'll simply call "workdays".
options intervalds=(workdays=mylib.workdays);
From there, all you have left to do is something like this:
data dateCalculations;
set mydata;
numOfDays = intck("workdays", theDate, today());
run;
SAS will take care of counting the number of dates (lines in the workdays dataset) separating the startdate (column called theDate) from the enddate (today's date).
Et voilĂ !
This is wonderful and very helpful. I use two different SAS systems (both on remote Unix servers). Setting the intervalds option only seems to work on one of them. I copy/paste the same code and on the other nothing happens - no warning, no error, it simply doesn't work.
Here is how I'm setting it (download the CSV from Yahoo! Finance for the S&P500, daily data, starting January 1950):
PROC IMPORT DATAFILE="sp500_1950_2016.csv"
OUT=sp500_1950_2016
DBMS=DLM
REPLACE;
delimiter=',';
getnames=yes;
RUN;
data trading_days;
set sp500_1950_2016 (keep = date rename=(date=begin));
where year(begin) < 2017;
run;
options intervalds=(TradingDay=trading_days) ;
Then I call it like so to count number of observations I should have from fund inception to Dec 31, 2016 or when the fund closed, whichever is sooner:
data ops2; set operations_master; where ~missing(inception);
if missing(enddate) then enddate = '31dec2016'd;
datadays = INTCK('TradingDay',inception,enddate);run;
proc univariate; var datadays;run;quit;
On system 1, this works just fine. On system 2, I get 0 for the variable datadays. I've already checked to see if there is a sys admin override on setting the intervalds option, and there is not. Is there another reason why this might not work on a given system?

How can I use different sets of date values depending on the date

I am looking to automate a daily report for my company but I have run in to a bit of trouble. The report gets updated only on the 2nd working day of each month. I found some code on the SAS website which works out what the 2nd working day of any month is.
data scdwrk;
/* advance date to the first day of the month using the INTNX function */
second=intnx('month',today(),0);
/* determine the day of the week using the WEEKDAY function */
day=weekday(second);
/* if day=Monday then advance by 1 */
if day=2 then second+1;
/* if day=Sunday then advance by 2 */
else if day=1 then second+2;
format second date9.;
run ;
I have also set a flag that compares todays date to the date from this generated by this piece of code.
I now need to find a way that if the code is run on the first working day of the month then it runs a particular set of macro date variables
%let start_date="&prevmnth;
%let end_date= &endprevmnth;
%let month= &prevyearmnth;
and then when its run on the 2nd working day of the month it uses the other set of macro date variables (calender month)
%let start_date="&currmnth;
%let end_date= &endcurrmnth;
%let month= &curryearmnth;
Any help on this would be greatly appreciated.
I have some recent code that does just this. Here is how I tackled it.
First, create a table of holidays. This can be maintained yearly.
Second, create a table with the first 5 days of the month that are not weekend days.
Third, delete holidays.
Finally, get the second value in the data set.
data holidays;
format holiday_date date9.;
informat holiday_date date9.;
input holiday_date;
datalines;
01JAN2015
19JAn2015
16FEB2015
03APR2015
25MAY2015
03JUL2015
07SEP2015
26NOV2015
25DEC2015
;
data _dates;
firstday = intnx('month',today(),0);
format firstday date date9.;
do date=firstday to firstday+5;
if 1 < weekday(date) < 7 then
output;
end;
run;
proc sql noprint;
delete from _dates
where date in (select holiday_date from holidays);
quit;
data _null_;
set _dates(firstobs=2);
call symput("secondWorkDay",put(date,date9.));
stop;
run;
%put &secondWorkDay;