Format of Date Variable Disappeared in Catx - sas

I am trying to create a SAS table for keeping descriptions and names of output tables which includes a formatted date inside. However the output includes date unformatted.
My code:
data tablenames;
infile datalines delimiter=',';
input description: $30. sastablename: $30.;
attrib datetoday format=yymmdd6.;
datetoday = date();
mergedtext=catx('_',sastablename,datetoday);
output;
datalines;
Table for Customers,TfC
Table for Sales,TfS
;
The code output gives TfC_20688 for mergedtext variable.
My desired output for mergedtext variable is TfC_160822.

You need to let CATX() know to use the formatted value. Try using the VVALUE() function if your variables are already formatted. Otherwise use the PUT() function to apply the format you want.
data tablenames;
infile datalines delimiter=',';
input description: $30. sastablename: $30.;
attrib datetoday format=yymmddn8.;
datetoday = date();
mergedtext1=catx('_',sastablename,vvalue(datetoday));
mergedtext2=catx('_',sastablename,put(datetoday,yymmddn8.));
datalines;
Table for Customers,TfC
Table for Sales,TfS
;
P.S. Don't use two digit years.

You can use the PUT() function to convert the SAS date in datetoday (the value of which is 20688) to the yymmdd format you want.
43 data tablenames;
44 infile datalines delimiter=',';
45 input description: $30. sastablename: $30.;
46 mergedtext=catx('_',sastablename,put(date(),yymmddn6.));
47 put mergedtext=;
48 output;
49 datalines;
mergedtext=TfC_160822
mergedtext=TfS_160822
NOTE: The data set WORK.TABLENAMES has 2 observations and 3 variables.

Related

SAS How convert string to time, when load data from csv?

Im trying load data from csv. I have a few formats: date, time, numeric, string. I dont have problem to convert data to this format except time format.
Data looks:
Date,Time,Transaction,Item
2016-10-30,9:58:12,1,Bread
2016-10-30,10:05:36,2,Scandinavian
2016-10-30,10:08:00,3,Hot chocolate
My code:
data lab0.piekarnia;
INFILE 'path_to_csv' delimiter=',' firstobs=2;
format Date yymmdd10.;
format Time time8.;
INPUT
Date yymmdd10.
Time time8.
Transaction
Item $;
run;
Result
What I try?
I try to manually convert string '12:22:22', This method give good results, but I dont know how can I implement it when load csv.
data ds1;
j = input('12:22:22',HHMMSS8.);
format j time8.;
run;
data have;
INFILE "path_to_csv" truncover delimiter=',' firstobs=2 ;
format Date yymmdd10.;
format Time time8.;
INPUT date time transaction item $32.;
informat
Date yymmdd10.
Time time.;
/*Instead input and informat statements you can use:
INPUT date:yymmdd10. time:time. transaction item $32.;
*/
run;
The first line has only 7 characters for the time value, but you told SAS to read exactly 8 characters. So it included the comma. When reading delimited data, like a CSV file, you need to use list mode input and not formatted mode. You do this by either eliminating the informat specification from the INPUT statement (and instead attach an informat to the variable with an INFORMAT statement) or by prefixing informat specification with the : (colon) modifier. Also if you don't define the length for ITEM (or give SAS something else, like an informat, that it can use to guess a length) it will be created as length $8.
input date :yymmdd10. time :time8. transaction item :$40.;

SAS Date Informat with Milliseconds

I am trying to create a SAS informat for the following date format:
"yyyy-mm-dd hh:ii:ss.SSS UTC", example: "2016-01-14 10:31:01.456 UTC"
I've gotten close using the following format code:
PROC FORMAT;
PICTURE MyDate other='%0Y-%0m-%0d %0H:%0M:%0s UTC' (datatype=datetime);
RUN;
Unfortunately when I try and use this as an INFORMAT I get an error "can't find format MyDate", as it hasn't been defined as an informat, just an output format.
I can try and create an informat from a dataset created from this format, but due to the milliseconds constraint it will only create values that map to times with .000 in the milliseconds section. For example:
DATA MyInDate ;
RETAIN FMTNAME "MyInputDate" type "I" ;
do label = "1jan2016:00:00:00"dt to
"2feb2016:00:00:00"dt by 1;
start = trim(left(put(label,MyDate.)));
output ;
end ;
RUN;
PROC FORMAT CNTLIN=MyInDate;
RUN;
Even if I were able to enumerate a dataset with milliseconds it would be prohibitively large. Since my dates can span years.
Is it possible to truncate my input data BEFORE passing it to the informat? I don't care about the milliseconds or the UTC qualifier. I can't change the input data.
EDIT: Using anydtdtm. as an informat results in empty values without error messages. Here is the data step making use of this informat:
DATA WORK.ImportDatTest;
LENGTH
'Event Time'n 8
;
FORMAT
'Event Time'n DATETIME25.
;
INFORMAT
'Event Time'n anydtdtm.
;
INFILE DATALINES DLM=','
;
INPUT
'Event Time'n : ANYDTDTM.
;
DATALINES;
2016-01-11 17:23:34.834 UTC
2016-01-11 17:23:34.834 UTC
2016-01-11 17:23:34.834 UTC
;
RUN;
Unfortunately, there is no way to create a picture informat in SAS currently. You would need to convert your data to a format SAS has a built-in informat for, or use a function or similar to format the data.
However, yours already is in such a format, so you shouldn't need to create an informat.
data test;
x="2015-10-05 10:12:24.333 UTC";
y=input(x,anydtdtm.);
put y= datetime17.;
run;
You can certainly truncate data while using an informat; by specifying a length in the informat, it will truncate to that length.
Here's an example using input from datalines:
data test;
infile datalines dlm=',' dsd;
input y :anydtdtm32.;
put y= datetime22.3;
datalines;
2015-10-05 10:12:24.333 UTC
2014-03-01 08:08:05.435 UTC
2013-01-01 23:02:05.445 UTC
;;;
run;

How to calculate 'age last birthday' on a given date for a given birthday in SAS PROC SQL step

I want to calculate 'age last birthday' on a specific evaluation date, given a specific date of birth, using a SAS PROC SQL command.
How can I do this and are there any limitations?
Sample Input
DATA INPUTS;
infile cards dlm=',' dsd;
INPUT DOBDt :DATE9. EvalDt :DATE9. expected;
FORMAT DOBDt date9. EvalDt date9.;
CARDS;
11MAY2009,10MAY2015,5
11MAY2009,11MAY2015,6
11MAY2009,12MAY2015,6
28FEB1984,01DEC2015,31
29FEB1984,28FEB2012,27
29FEB1984,29FEB2012,28
29FEB1984,01MAR2012,28
;
RUN;
The goal would be to take the dobDt as an input, evaluate on the EvalDt and produce the answer of expected
This can be done as such :
PROC SQL
PROC SQL;
CREATE TABLE outputs2 AS
select
*
,intck('year',DOBDt,EvalDt,'c') AS actual
,((calculated actual) eq expected) AS check
FROM
inputs
;
QUIT;
actual, the calculated value, matches expected, the desired outcome, for all the examples provided. I am not aware of any limitations to this approach although there are probably some extreme ages that it cannot calculate due to SAS dates having a limited range of values.
As a bonus:
DATA STEP
DATA outputs;
set inputs;
actual = intck('year',DOBDt,EvalDt,'c');
check = (actual eq expected);
RUN;
This is how we used to do it back in the day. Also "age at last birthday" seems pretty clear to me.
DATA INPUTS;
infile cards dlm=',' dsd;
INPUT DOBDt :DATE9. EvalDt :DATE9. expected;
FORMAT DOBDt date9. EvalDt date9.;
age = year(evaldt)-year(dobdt) - (month(evaldt) eq month(dobdt) and day(evaldt) lt day(dobdt)) - (month(evaldt) lt month(dobdt));
CARDS;
11MAY2009,10MAY2015,5
11MAY2009,11MAY2015,6
11MAY2009,12MAY2015,6
28FEB1984,01DEC2015,31
29FEB1984,28FEB2012,27
29FEB1984,29FEB2012,28
29FEB1984,01MAR2012,28
;;;;
RUN;
proc print;
run;

dataset using datalines infile with two dates variables

My datalines has 2 variables, date 1 and date 2 with corresponding format ddmmyy10. and mmddyy10.
data date;
input date1 ddmmyy10. date2 mmddyy10.;
datalines;
09/01/2015 01/09/2015
10/01/2015 01/10/2015
11/01/2015 01/11/2015
12/01/2015 01/12/2015
13/01/2015 01/13/2015
;
run;
I tried to add the code but still not work
infile datalines delimiter=' ';
The : (colon) format modifier enables you to use list input but also to specify an informat after a variable name, whether character or numeric. SAS reads until it encounters a blank column, the defined length of the variable (character only), or the end of the data line, whichever comes first.
Format Modifier
data date;
input date1: ddmmyy10. date2: mmddyy10.;
datalines;
09/01/2015 01/09/2015
10/01/2015 01/10/2015
11/01/2015 01/11/2015
12/01/2015 01/12/2015
13/01/2015 01/13/2015
;
run;
The reason why the code is not working is because you are using List input to read non-standard data without colon input modifier or informat statement.
For non-standard data(commas, dollar, date etc. -> Reading Raw data -> Kinds of Data ) or standard data of length greater than 8 byte using List input technique you would need to use either INFORMAT statement or colon modifier with INPUT statement.
1) Assign informat for the input variables using INFORMAT statement or ATTRIB statement
data date;
informat date1 ddmmyy10. date2 mmddyy10.;
input date1 date2;
format date1-date2 yymmdd10.;
datalines;
09/01/2015 01/09/2015
10/01/2015 01/10/2015
;
run;
2) Use Colon (:) input modifier
data date;
input date1: ddmmyy10. date2: mmddyy10.;
format date1-date2 yymmdd10.;
datalines;
09/01/2015 01/09/2015
10/01/2015 01/10/2015
;
run;

using dsd and comma informat at the same time

I have the below raw data
1,,35,000
2,100,45,000
and need the below in a dataset
1 . 35000
2 100 45000
this would require both dsd option and using comma. informat.
How to carry this out?
DSD has nothing to do with this - DSD involves input like
1,,"35,000"
2,100,"45,000"
If that is what you have, then you can use the : operator to read it in with the comma informat.
data test;
infile datalines dlm=',' dsd;
input id
num
dollar :comma8.;
datalines;
1,,"35,000"
2,100,"45,000"
;;;;
run;
If you do not have the quotes around the field, then you will need to parse this somehow. One solution is below, which will work as long as the field with commas is the final field.
data test;
infile datalines dlm=',' dsd;
input #;
if countc(_infile_,',') =3 then do;
_commapos = findc(_infile_,',',-1*length(_infile_));
_infile_ = substr(_infile_,1,_commapos-1)||substr(_infile_,_commapos+1);
end;
input id
num
dollar ;
put _all_;
datalines;
1,,35,000
2,100,45,000
;;;;
run;
If the field your potential is in is in a consistent field, but NOT the first one, you can modify the above solution to correct it. If it's in potentially more than one field, you have a much more difficult problem to solve.