How to create a datetime macro variable in SAS - sas

%let mydate = "01JUN2021 00:00:00.000"dt;
This does not work. How do I create a datetime macro variable without using proc sql or data step?

The pure macro solution is:
%let mydate = %sysfunc(dhms(%sysfunc(mdy(6,1,2021)), 0, 0, 0));
%put &=mydate; * PRINTS THE UNFORMATTED VALUE STORED;
%put %sysfunc(sum(&mydate), datetime22.); * PRINTS THE DATETIME VALUE FORMATTED;
Output:
MYDATE=1938124800
01JUN2021:00:00:00
You can of course perform the dhms() and mdy() functions on separate lines if that is clearer for you.
Compare this to what your orginal code is doing:
%let mydate="01jan2021:00:00:00"dt;
%put &=mydate;
Prints:
MYDATE="01jan2021:00:00:00"dt
Notice how in your approach the string "01jan2021:00:00:00"dt has been saved into the macro variable, rather than the actual numeric date value 1938124800? Sometimes when you use your approach SAS gets confused when you try to use the value and it is unable to translate the literal to a numeric date value.

try %let mydate = '1Jan2021:0:0:1'dt
Note that it uses single quotes & theres no space between date and time

Your posted macro variable works fine in SAS code.
82 %let mydate = "01JUN2021 00:00:00.000"dt;
83
84 data test;
85 now = datetime();
86 then = &mydate;
87 diff = intck('dtday',then ,now);
88 format now then datetime20. ;
89 put (_all_) (=);
90 run;
now=16JUN2021:08:18:33 then=01JUN2021:00:00:00 diff=15
NOTE: The data set WORK.TEST has 1 observations and 3 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds
If you need to use the value in pass through SQL code then you will need to set the macro variable to text that the remote database's implementation of SQL will recognize as a datetime value.
So perhaps
%let myts = timestamp '2021-06-01 00:00:00.000';

Related

How to get the current month and year in YYYYMM format in SAS?

I want to use Call Symput and define a variable called "Current_Month" which will give me the year and month in YYYYMM format. For instance, for the current month Feb'2023, the output should come as 202302. Any help would be appreciated!
Using straight macro logic:
today() function to generate today's date
sysfunc() to call the today() function within the macro. Note the second parameter to sysfunc() can be a format to format the output.
%let currentMonth = %sysfunc(today(), yymmn6.);
%put &currentMonth.;
log:
68
69 %let currentMonth = %sysfunc(today(), yymmn6.);
70
71 %put &currentMonth.;
202302
You can use call symputx() in a data step to generate a macro variable. (you would only want to use the older call symput() routine if you need the macro variable to contain trailing or leading spaces).
You can use the YYMMN6. format to display a date value as a digit string where the digits represent the four digit year and two digit month of the date.
data _null_;
call symputx('current_month',put(today(),yymmn6.));
run;

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'

SAS - append string macro variable to data set name

I'm trying to append a string macro variable to a data set name in SAS. I want to create datasets that read something like work.cps2020jan and work.cps2020feb. But that's not what I am getting. My code:
%macro loop(values);
%let count=%sysfunc(countw(&values));
%do i = 1 %to &count;
%let value=%qscan(&values,&i,%str(,));
%put &value;
data work.cps2020&value.;
set "A:\cpsb2020&value" ;
mth = "&value.";
keep
PEMLR
mth
;
run;
%end;
%mend;
%loop(%str(jan,feb));
Running this code results in the following output in the log:
NOTE: There were 138697 observations read from the data set
A:\cpsb2020jan.
NOTE: The data set WORK.CPS2020 has 138697 observations and 2 variables.
NOTE: The data set WORK.JAN has 138697 observations and 2 variables.
NOTE: DATA statement used (Total process time):
real time 4.29 seconds
cpu time 0.20 seconds
feb
NOTE: There were 139248 observations read from the data set
A:\cpsb2020feb.
NOTE: The data set WORK.CPS2020 has 139248 observations and 2 variables.
NOTE: The data set WORK.FEB has 139248 observations and 2 variables.
NOTE: DATA statement used (Total process time):
real time 4.44 seconds
cpu time 0.15 seconds
I don't understand why my macro creates two datasets per loop instead of one dataset per loop called work.cps2020jan and work.cps2020feb. If I change &value. to &i. SAS outputs work.cps20201 and work.cps20202. But that's not what I want.
Any insights?
The %QSCAN macro function will mask it's result with special invisible (non-printable) characters only visible to the macro processor system.
What happened is that
data work.cps2020&value.;
was seen as
data work.cps2020<mask-character><non-masked part of symbol value><mask-character>;
during executor processing, which treated the non-printable mask character as a non-syntax token separator, resulting in a DATA statement listing two output tables.
data work.cps2020 jan;
The positions of mask characters in a macro variable can be observed (in the LOG) using %put _user_, or, the actual symbol contents can be captured from a metadata view such as SASHELP.VMACRO or DICTIONARY.MACRO
Let's simplify your macro and add some logging and symbol capture
%macro loop(values);
%local count i;
%let count=%sysfunc(countw(&values));
%do i = 1 %to &count;
%let value=%qscan(&values,&i,%str(,));
%put _user_; %*--- log them masks;
data x&i; %* --- symbol capture;
set sashelp.vmacro;
where name like '%VALUE%';
value_hex = put (value,$HEX40.);
run;
%* --- do the step that creates two tables;
data work.cps2020&value.;
set sashelp.class;
run;
%end;
%mend;
options nomprint nosymbolgen nomlogic;
%loop(%str(jan,feb));
proc print data=x1 noobs style(data)=[fontsize=14pt fontfamily="Courier"];
var value:;
run;
LOG snippet, those little boxes are the special invisible masking characters (I am showing them in image captures because stack overflow / html won't show non-printable characters)
Same LOG text, copy and pasted into Notepad2 show the mask characters as control characters
The Proc PRINT of the captured macro symbol data will expose the hexadecimal masking characters
06 macro %quote start
08 macro %quote end
01 macro %str start
02 macro %str end
1E masked version of comma
&value is returned as quoted by %qscan(). Use %scan() instead. Quoted macro variables can sometimes cause issues on resolution when they're used in this way. It's best to only quote them when needed, such as in a %put statement that has a % sign within it.
You don't need %qscan(). If the value contained any characters that need macro quoting then they would be invalid for use in a member name anyway. So use %scan() instead.
But when used inside of a macro the tokenizer will sometimes mistakenly see things like xxx&mvar as two tokens even when there are no special characters in &mvar. You can group the value you are generating to work around that.
For example by making a new macro variable
%let dsn=cps2020&value.;
data work.&dsn. ;
Or use the %unquote() function:
data %unquote(work.cps2020&value.);
Or use a name literal:
data work."cps2020&value."n;

SAS, Make a macro variable contains YYYYMM

I have a SAS statement.
%let data = SampleData_200001_201603;
The data name is changing every time I run the code. For example:
run in 201604 then name= SampleData_200001_201603;
run in 201605 then name= SampleData_200001_201604;
run in 201606 then name= SampleData_200001_201605;
How can I write %let data = ; to make it automatic? Thanks.
If you are using the current date then you can use the YYMMN6. format to get the date in that format.
%let data = SampleData_200001_%sysfunc(date(),yymmn6);
If you need the previous month then use INTNX() function.
%let data = SampleData_200001_%sysfunc(intnx(month,%sysfunc(date()),-1),yymmn6);
%let date = 201603;
%let data = SampleData_200001_&date.;
and loop through a list of dates
I had enough of multiple %sysfunc's to get what I wanted. I wrote a macro routine that allows assignation of dates to macro variables using the format we want. No twists and turns with %sysfunc.
The code is here. And it lets you do that: assign to a macro variable named "thisMonth" the value of today(), with format YYMMn6. :
%letdate(thisMonth, today(), fmt=YYMMn6.)
log says:
*** Variable macro thisMonth = 201604 ***
And then
%let myFile = someName_&thisMonth;
%put &myFile;
log says:
someName_201604
The macro is documented in French but it should be self-explanatory for the most part.
EDIT:
Note that you can use any function normally reserved to data steps as the second parameter:
%letdate(firstDayLastMonth, intnx("MONTH", today(), -1, "BEGIN"), date9.)
log says:
*** Variable macro firstDayLastMonth = 01MAR2016 ***

Get data set with maximum date in name by proc SQL

Suppose I have some data sets in library lib, their names look like Table_YYYYMMDD (e.g. Table_20150101).
I want to get a name of a data set with maximum date (YYYYMMDD) and store it in a macro variable.
I'm using proc sql and from dictionary.tables.
First I extract a YYYYMMDD part of name. Then I should convert it to date and then find MAX. And I want to be sure that I have at least one data set in library.
proc sql;
select put(MAX(input(scan(memname, 2, '_'), yymmdd8.)), yymmddn8.)
into :mvTable_MaxDate
from dictionary.tables
where libname = 'LIB';
quit;
So,
Is it right to use sas functions like scan in proc sql?
How could I check whether the query is not empty (mvTable_MaxDate hasn't missing value)?
Thanks for your help:)
The cause of the error is that your are using the INPUTN() function, which expects the second argument to be a text literal or the name of a variable. If you change to INPUT(), it will avoid the error.
Also note you need to upcase the literal value of the library name on your where clause. Dictionary.tables stores libnames in upcase.
As written, the value of the macro variable will be a SAS date value. If you want it formatted as YYMMDDN8. you will need to add that.
Here's an example:
74 data a_20151027
75 a_20141022
76 a_20130114
77 ;
78 x=1;
79 run;
NOTE: The data set WORK.A_20151027 has 1 observations and 1 variables.
NOTE: The data set WORK.A_20141022 has 1 observations and 1 variables.
NOTE: The data set WORK.A_20130114 has 1 observations and 1 variables.
80
81 proc sql noprint;
82 select COALESCE(MAX(input(scan(memname, 2, '_'), yymmdd8.)), 0)
83 into :mvTable_MaxDate
84 from dictionary.tables
85 where libname = 'WORK';
86 quit;
87
88 %put &mvTable_MaxDate;
20388
89 %put %sysfunc(putn(&mvTable_MaxDate,yymmddn8));
20151027
As a side-comment, often life becomes much easier if you can just combine all your data into one dataset, and store the dataset name date suffix as a variable.