How to convert SAS E dates into readable dates - sql

Hi I'm working in SAS platform and I've a data_set with more then 30 columns. there are two date columns in that data-set. dates in that data set are in format as 1.33E12
This is the little part of my table
I want to create a new data-set with few columns and then I'm exporting it to excel file.
my code is
dataset
othercolumns | date1 | date2
- 1.33E12 2.53E14
proc sql noprint;
create table my_data_set as
select ID, col_1, col_2, date1, date2
from data_set;
quit;
I want my date values in date1 and date2 column in a readable format like 10feb2017 as date9. SAS date format so they can be exported to my excel file. right now with E power dates I'm getting ####### as date1 and date2 columns in excel
I've tried
select ID, col_1, col_2, datepaart(date1), datepart(date2)
Warning: Invalid Argument, getting '.' values in date column
select ID, col_1, col_2, date1 date9., date2 date9.
select ID, col_1, col_2, date1 DATEw., date2 DATEw.
Error: Syntax error
select ID, col_1, col_2, date1 format=DATE9., date2 format=DATE9.
Getting the same E date values in my table
select ID, col_1, col_2, put(date1 , date9. ), put(date2 , date9.)
Error: Date value out of range
How to convert the E date into a readable format into my table so i can export it to excel?
this is my export code
ods excel file ="C:\data.xlsx";
ods excel close;
proc export
data = work.my_data_set
dbms = xlsx
outfile = "C:\data.xlsx"
replace;
quit;

data have;
unix_ts = 253402300799;
put unix_ts= datetime21.;
sas_dt = unix_ts + '01JAN1970:0:0'DT ;
put sas_dt= datetime21.;
run;
proc sql;
create table want as
select
(
case
when unix_ts + '01JAN1970:0:0'DT > '27FEB8000:0:0'DT then unix_ts + '01JAN1970:0:0'DT - 2 * 86400
when unix_ts + '01JAN1970:0:0'DT > '28FEB4000:0:0'DT then unix_ts + '01JAN1970:0:0'DT - 1 * 86400
else unix_ts + '01JAN1970:0:0'DT
end
) as sas_date format=datetime21.
from have;
quit;
Rather than cutting and pasting you should understand what is going on with the case statement and the 01-JAN-1970
253,402,300,799
Unix timestamp, seconds from 01-JAN-1970, representing 31-DEC-9999:23:59:59
Likely sentinel value contained in valid_to that OP imprecisely shows as
2.534E14
Date columns presumed to be Unix time stamps.
253,717,747,199
SAS datetime value '31-DEC-9999:23:59:59'DT is seconds from 01-JAN-1960
Timestamp conversion
Unix timestamp values are epoch 01-JAN-1970:0:0:0
SAS datetime values are epoch 01-JAN-1960:0:0:0
So one would presume a SAS values are 10 years (in seconds) greater than Unix value.
The simple approach is to add the epoch base differential to the Unix timestamp to achieve the SAS datetime
SAS_DT = UNIX_TS + '01JAN1970'DT; *Naive conversion;
However, this is incorrect because Unix and SAS calendaring disagree on some leap years!
253,402,300,799 is 31-DEC-9999:23:59:59 per https://www.epochconverter.com/
253,717,747,199 is '31-DEC-9999:23:59:59'DT
difference, 315,446,400 should be SAS '01-JAN-1970:0:0'DT. But the difference is actually '30DEC1969:00:00'DT.
So, adding the epoch baseline differential to a far off Unix timestamp will result in a SAS datetime that does not represent the same calendar point as in Unix.
In other words 253,402,300,799 + '01-JAN-1970:0:0'DT is '02-JAN-10000:0:0'DT -- two days beyond the expected Unix sentinel
Or, after about 8,000 years, the calendar accounting systems in Unix and SAS will deviate by 2 days.
Calendaring deviation
Unix calendaring considers year 4000 to be a leap year, 29-FEB-4000 is valid.
SAS calendaring incorrectly considers 4000 to be a non-leap year, '29-FEB-4000'DT is invalid.
ly4000 = '29-FEB-4000:0:0'DT;
-------------------
77
ERROR: Invalid date/time/datetime constant '29-FEB-4000:0:0'DT.
ERROR 77-185: Invalid number conversion on '29-FEB-4000:0:0'DT.
The same deviation happens again in year 8,000.
The least damaged conversion of Unix timestamp to SAS datetime takes the naïve conversion and subtracts one day for each misaligned leap-day determination in the time frame.

It looks as though you have two different types of UNIX timestamps, which count the number of milliseconds or microseconds from a particular date and time - usually 1st January 1970 00:00:00.000000. Without knowing exactly what sort of timestamps they are, I can only make an educated guess as to how to convert them to human-readable dates. Here are a few possible interpretations:
data example;
date1=2.53e14;
date2=1.33e12;
run;
proc sql;
create table want as
select
intnx('year',datepart(date/1e3),10,'s') format = yymmdd10. as date1a,
intnx('year',datepart(date/1e6),10,'s') format = yymmdd10. as date1b,
intnx('year',datepart(date2/1e3),10,'s') format = yymmdd10. as date2a,
intnx('year',datepart(date2/1e6),10,'s') format = yymmdd10. as date2b
from example;
quit;
The logic here is:
Divide by 1000 or 1000000 to convert to seconds
Interpret the result as a SAS datetime value counting the number of seconds from 1st January 1960 00:00:00
Extract the date component from the datetime
Add 10 years to convert to the UNIX epoch
Hopefully one of these looks right to you.

Related

Subtracting dates in Proc SQL/SAS

I have a Proc SQL code block running in SAS Enterprise Guide. I need to find the number of days between two dates
proc sql;
select col_A,col_B,col_C, (put (date(),yymmdd10.) - col_C) as age_bucket
from DB2.Table_A;
quit;
col_C is a date of the 'YYYY-MM-DD' format (e.g. 2022-05-02)
I am subtracting col_C from today's date and want to get the total number of days between them in as age_bucket. I am getting the following error.
ERROR: Expression using subtraction (-) requires numeric types.
How do I go about this?
The Table_A is from DB2 database.
Using the PUT() function to convert the current date into a character string is going to make it impossible to perform arithmetic with the result.
If COL_C has a DATE value it does not matter how the value is displayed (formats just impact how values are displayed). A DATE value is just the number of days since 1960. You only need to subtract the two numbers to calculate the difference in days.
(date() - col_C) as age_bucket
If COL_C has a DATETIME value (number of seconds since 1960) then first convert it to a DATE value.
(date() - datepart(col_C)) as age_bucket
If COL_C is a character string in the style YYYY-MM-DD then use the INPUT() function with the YYMMDD informat to convert the string into a date value.
(date() - input(col_C,yymmdd10.)) as age_bucket
I think DB2 supports the DAYS_BETWEEN() function which will give you the number of days between the 2 date arguments.
https://www.db2tutorial.com/db2-date-functions/
Use YRDIF Function to get age in years. Subtracting dates will get your age in days.
Both dates should be SAS dates, numeric with a date format.
proc sql;
select col_A,
col_B,
col_C,
floor(YRDIF(input(col_C, yymmdd10.), today(),"AGE")) as age_bucket
from DB2.Table_A;
quit;

Teradata: Date value division with 365 days

Needs an update what exactly the below query does when it divisible by 365??
select CAST((FLGHT_DATE )/365 AS INTEGER);
Note: FLGHT_DATE column is of date type and in YYYY-MM-DD format.

How to add Date column in using SAS' proc sql?

I am trying to add the Today's Date column to my existing dataset. I am using the method provided in my previous question. My code is as follows:
proc sql;
alter table data1 add today_date char label= "Today's Date" format = $8.;
update data1 set today_date= today();
quit;
However, I get the following error:
327 proc sql;
328 alter table data1 add today_date char label= "Today's Date" format = $8.;
NOTE: Table DATA1 has been modified, with 11 columns.
329 update data1 set today_date= today();
ERROR: today_date, a character column, can only be updated with a character expression.
330 quit;
The purpose of this is to calculate the tenure in days for the opened accounts existing in the dataset. The tenure, per my understanding, can calculated using the difference between the Today's Date and when the account was opened.
Thanks
Regarding
I am trying to add the Today's Date column to my existing dataset.
A data set has a fixed value for each cell (intersection of row and column). Today's date value tomorrow is yesterday's date value.
A data structure that has dynamic values is known as a view, and some columns in it can be the result of a calculation.
You might want to consider
* Create view once, use tomorrow! ;
proc sql;
create VIEW data1_v as
select
*
, today() as today_date format=date9. label= "Today's Date"
from
data1
;
You might also want to learn more about the nature of SAS date values and SAS date formats.
SAS Date value - numeric, the number of days since 01JAN1960
SAS date format - how to render a date value (as a sequence of glyphs) for humans.
SAS date informat - how to interpret glyphs and convert them into a date value
You can also refer to today's date in computation by simply using the today() function and not rely on a precomposed or precalculated column in a data source.
I suspect the issue is with the fact that your Today's Date field is in the wrong format, not as date. try this code below:
proc sql;
alter table data1
add todays_date date;
update data1
set todays_date = today();
quit;

How to convert a binary date column date into gregorian date format in sql

This i got to convert a single binary date to Gregorian date ,
Select to_char(to_date(sum(2415020+42744),'J'),'MMDDYYYY') Last_arrear_date from dual;
But I'm not able to convert a binary date column date into Gregorean in xyz table
For Example
Suppose i have a table borm,That has account_open_date column that stores data in binary format , I want to convert that account_open_date column data into Gregorian date format. Please let me know how to convert that
While you have to change the entire column, Don't use the sum function.
Try the below SQL command it will work.
select acct_no, to_char(to_date((account_open_date + 2415020), 'J'),'DD-MON-YYYY')
from borm;
To Change Binary to Gregorian
select column1 ,...columnN, to_char(to_date((Column_name + 2415020), 'J'),'DD-MON-YYYY')
from Table_Name
WHERE Condition;
To change Gregorian to Binary:
select column1,columnN, to_char(to_date(Column_Name,'YYYYMMDD'),'J')-2415020
from Table_Name
where condition ;

Between operation for date in SQLite database

I have a table student with the following columns:
no - integer
name - string
startdate - date
enddate - date.
Date format is MM/DD/YYYY.
I will give a date as input. Now I need a query the inputdate which found in between the start and end date.
For an example I will give 04/14/2012, then the query should return the 1st record as in the figure.
(because input date (04/14/2012) is found in between the 04/10/2012 to 04/20/2012)
Please help me.
The issue you are having is caused by your assumption that sqlite has a date/datetime type when in fact it doesn't.
I suggest you read the following http://www.sqlite.org/datatype3.html to have a better understanding of sqlite types.
The dates in the MM/DD/YYYY format are handled as TEXT by sqlite, and so those dates are compared as strings. For example, 02/01/2012 is considered bigger than 01/02/2012by sqlite if compared directly.
You will need to transform those dates to a format that can be string-compared. Here is an example:
sqlite> create table foo (d TEXT);
sqlite> insert into foo values ('02/01/2012');
sqlite> select substr(d, 7, 4) || substr(d, 1, 2) || substr(d, 4, 2) from foo;
20120201
You should post what you have tried so far.
There should be a between clause that you can use:
select * from table
where inputdate between startdate and enddate
Dates as a date type in SQLite don't exist. There are a number of approaches to dealing with dates - store them as integer seconds since 1 Jan 1970 (unixepoch) or store them as strings, but if you do, then you really need to store them in 'YYYY-MM-DD' format because that is what the date functions require as input.
Assuming you use the string format in the format I suggested then your query would look something like
SELECT * FROM Table WHERE Date(Inputdate) BETWEEEN Date(startDate) AND Date(EndDate);
(although you may want to format the output of the date columns to US date format with
SELECT Strftime("%m/%d/%Y",startDate) As StartDate ...
If you use seconds since 1970 its somewhat easier because the seconds just compare without needing the convert them to dates, although you still might want to output in US date format, so ...
SELECT Strftime("%m/%d/%Y",startDate) As StartDate ... FROM Table WHERE inputDate BETWEEN startDate and EndDate;
sqlite> select *from tbl_node where mydate between '2014-02-02' and '2014-02-06';
it show the output :-
1|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:00:44
2|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:01:03
3|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:00:57
4|1|123|456|12eb-ab|1|1|254|123|19|2014-02-03 16:00:34
Here mydate is column name in tbl_node;
we can also use from current time , using now.
sqlite> select *from tbl_node where mydate between '2014-02-02' and 'now';