I am block-inserting data from Stata (a statistics package) into a Teradata database. I am having trouble converting dates and timestamps from Stata's native format to Teradata's.
Stata stores dates as days since 01/01/1960, so that 01jan1960 is 0 and 02jan1960 is 1. Timestamps are stored as milliseconds since 01jan1960 00:00:00.000, so that 1000 is 01jan1960 00:00:01. Here are some examples:
timestamp Stata's tstamp date Stata's date
2015-04-13 03:07:08 1744513628000 2015-04-13 20191
2015-04-14 19:55:43 1744660543000 2015-04-14 20192
2015-04-08 11:41:39 1744112499000 2015-04-08 20186
2015-04-15 06:53:34 1744700014000 2015-04-15 20193
I tried 2 approaches. The first involves converting the dates/timestamps to strings in Stata before inserting and then doing something like this once the data is inserted:
ALTER TABLE mytable ALTER date_variable DATETIME
However, I cannot figure out how to do the second part from the documentation I have and after searching the various fora.
The second approach is leaving the dates and timestamps as integers, and then doing some of conversion once the integers are inserted. Perhaps I can also pre-convert dates in Stata to TD's internal format with:
gen td_date = ((year(stata_dt)-1900)*10000 + month(stata_dt)*100 + day(stata_dt))
However, I am not sure what the formula for timestamps would be. I am also not sure how to do the second part (making the integers into dates/timestamps).
You can't change the datatype of a column in Teradata from string to date/timestamp.
But when you insert a string into a date/timestamp column there will be an automatic typecast. So simply convert to a string with 'yyyy-mm-dd' or 'yyyy-mm-dd hh:mi:ss' format.
You could also do the conversion during load on Teradata using calculations, but IMHO the 1st solution is preferable:
-- add the number of days to the start date
DATE '1960-01-01' + stata_dt
-- I use a similar approach for Unix Timestamps starting 1970 :-)
-- split into days and seconds
CAST(DATE '1960-01-01' + (stata_ts / 86400000) AS TIMESTAMP(0))
+ ((stata_ts MOD 86400000 / 1000) * INTERVAL '00:00:01' HOUR TO SECOND)
Related
My question is really simple, hope someone deigns to answer!.
Being very to new SAS, the date and its formats really is confusing me.
I have timestamp column from which I need to substract 2 days while keeping its datatype
The value of the column is "2022-04-20-19.37.57.714699"
What I need is "2022-04-18-19.37.57.714699"
When I try this I get number datatype:
PROC SQL;
CREATE TABLE my_table AS
SELECT
cust_id,query_date, (query_date)-2 as calc_date
FROM other_table
;quit;
I try format,datetime function, but ended up with "Statement is not valid or it is used out of proper order"
Thanks
Assuming that the QUERY_DATE variable is numeric and has datetime values in it (the number of seconds since 1960) then you can use the INTNX() function with the DTDAY interval to adjust the value by two days. To keep the same time of day use SAME for the alignment parameter.
intnx('dtday',query_date,-2,'same')
Alternatively you could just subtract 48 hours worth of seconds from the value.
query_date -2*'24:00:00't
If you want the values to display in a human readable way then attach any of the many datetime formats, such as DATETIME to the new variable.
CREATE TABLE my_table AS
SELECT cust_id,query_date
, intnx('dtday',query_date,2,'same') as calc_date format=datetime20.
FROM other_table
;
If the variable is just a string then you cannot subtract from strings. You will have to convert the strings into numbers to perform arithmetic. You probably have too many decimal places for SAS datetime informats/formats to replicate (and perhaps to be uniquely stored in a floating point value) so just convert the date part and then append back the rest to keep the same time of day. Since dates are stored as number of days you can just subtract the 2 days using normal subtraction.
put(input(query_date,yymmdd10.)-2,yymmdd10.)||substr(query_date,11)
This should solve your problem. The first issue to tackle is converting the date string into a sas datetime. The input() function with the anydtdtm. informat accomplishes that with a small caveat as seen in the output.
data test;
date_txt = '2022-04-20-19.37.57.714699';
query_date = input(date_txt, anydtdtm.); * convert string into sas datetime;
calc_date = intnx('dtday', query_date, -2, 's'); * backup 2 days preserving the time;
format query_date calc_date e8601dt26.6;
run;
date_txt
query_date
calc_date
2022-04-20-19.37.57.714699
2022-04-20T19:37:57.000000
2022-04-18T19:37:57.000000
The default width of the informat is 19 characters which excludes the fractional seconds, but the informat correctly converted the string into a datetime.
To attempt to capture the full width of the date string, I modified the informat to anydtdtm26.. However, that change resulted in an error and missing values for query_date and calc_date. Although the anydtdtm informat is robust with converting a wide variety of date and time formats, I suspected that the problem lies with the periods used to delimit the hours and minutes.
To correct that problem I used prxchange() function to replace the periods after hours and minutes with colons which are standard time componenent delimiters. That change allows the anydtdtm informat to properly convert the fractional seconds.
data test2;
date_txt = '2022-04-20-19.37.57.714699';
date_mod = prxchange('s/\./:/', 2, date_txt); * replace first 2 periods w/ colons;
query_date = input(date_mod, anydtdtm26.); * convert string into sas datetime;
calc_date = intnx('dtday', query_date, -2, 's'); * backup 2 days preserving the time;
format query_date calc_date e8601dt26.6;
run;
date_txt
date_mod
query_date
calc_date
2022-04-20-19.37.57.714699
2022-04-20-19:37:57.714699
2022-04-20T19:37:57.714699
2022-04-18T19:37:57.714699
Although I used a data step to illustrate the solution the functions can also be used in a SQL statement.
I am compiling data of multiple years but I only need time not date and year. How can I set the year and date data into a single year and a single date so that I can use the time information as accumulative data?
2020-01-01 + time value.
Thanks!!!!
The information you provided is a little light. Still with certain assumptions:
create table ts_sum (ts_fld timestamptz);
insert into ts_sum values ('2020-04-14 08:15:32'), ('2021-09-27 18:45:01'), ('2022-01-09 20:21:05');
select sum(to_char(ts_fld, 'HH24:MI:SS')::interval) from ts_sum;
sum
----------
47:21:38
Needs to be tested with your data. The procedure is extract the time portion out of the timestamp using to_char then cast that to an interval and then sum the time intervals.
I am trying to convert a string on the on the following format to a timestamp in DB2: 2015-09-07T09:15:25.4788396+04:00
Problem is, DB2 only seems to handle 6 digit fractional seconds, not 7 as in my case. Any thoughts on a good workaround?
The format you have there matches the xs:dateTime pattern from XML. You can use this to your advantage and use implied XML parsing:
SELECT XMLCAST(XMLCAST('2015-09-07T09:15:25.4788396+04:00' AS XML) AS TIMESTAMP)
FROM SYSIBM.SYSDUMMY1
1
--------------------------
2015-09-07-05:15:25.478839
Note the timestamp returned is in UTC, add + CURRENT TIMEZONE to return it as a local timestamp. Tested on DB2 z/OS DSN10015.
You will need to parse the timezone from the string. Once you have that you can get the UTC time from subtracting from "current timezone" then add the parsed timezone to it as shown below. You will also need to use REPLACE to change T to a dash and colons to dots.
SELECT (TIMESTAMP('2015-09-07-09.15.25.4788396') - (current timezone)) + 4 hours FROM sysibm.sysdummy1;
I have a date field, which is just text, and a field showing seconds since midnight.
CREATE TABLE t ("s" text, "d" text, "ts" timestamp)
;
INSERT INTO t ("s", "d")
VALUES
(24855, 20130604),
(24937.7, 20130604)
;
How can I convert these fields into a timestamp, so I can run time-based queries?
See a demo with SQL Fiddle:
http://sqlfiddle.com/#!15/67018/2
Since your data includes fractional seconds, you need to adjust for those:
UPDATE t
SET ts = to_timestamp(d||s, 'YYYYMMDDSSSS.MS');
Assuming milliseconds as the maximum precision. Else employ US for microseconds.
Consider the fine print in the manual:
In a conversion from string to timestamp, millisecond (MS) or
microsecond (US) values are used as the seconds digits after the
decimal point. For example to_timestamp('12:3', 'SS:MS') is not 3
milliseconds, but 300, because the conversion counts it as 12 + 0.3
seconds. This means for the format SS:MS, the input values 12:3,
12:30, and 12:300 specify the same number of milliseconds. To get
three milliseconds, one must use 12:003, which the conversion counts
as 12 + 0.003 = 12.003 seconds.
Also note that to_timestamp() returns timestamp with time zone, which is coerced to timestamp in the context of the update, which turns out all right. For other contexts, you may want to cast explicitly or use this alternative, yielding timestamp:
SELECT d::date + interval '1s' * s::numeric AS ts FROM t;
The plain cast to date (d::date) works because your dates are in standard ISO 8601 format - or so I assume.
Concatenate the two fields together and then use to_timestamp()
UPDATE t SET ts = to_timestamp(d||s, 'YYYYMMDDSSSS');
For a discussion of the formatting string used in to_timestamp, see Table 9-21: Template Patterns for Date/Time Formatting in the PostgreSQL documentation.
I have the following from a Microsoft SQL Server database for date/time value:
0x00009CEF00A25634
I found this post:
Help me translate long value, expressed in hex, back in to a date/time
Which seemed to be on the right track but by using the code I didn't get the right dates, are my hex dates in a different format? How would I convert them to a normal date, I am using PHP/PostgreSQL.
select CAST (0x00009CEF00A25634 as datetime) gives 2009-12-30 09:51:03.000
This is two integers. One for the date part 0x00009CEF (decimal 40175) and one for the time part 00A25634 (decimal 10638900). The date part is a signed integer giving number of days since 1 Jan 1900. The time part is an integer representing number of ticks.
There are 300 ticks in a second.
It can be seen that the following also returns the same result
SELECT DATEADD(MILLISECOND,10638900*10/3.0, DATEADD(DAY,40175, '19000101'))
You will need to figure out how to apply this to postgres.
Edit: an answer here apparently does this. I haven't tested it myself.
This works for me while migrating from SQL to MySQL :
SELECT (CAST('1900-01-01 00:00:00' + INTERVAL CAST(CONV(substr(HEX( 0x0000A249004576D0 ),1,8), 16, 10) AS SIGNED) DAY + INTERVAL CAST(CONV(substr(HEX( 0x0000A249004576D0 ),9,8), 16, 10) AS SIGNED)* 10000/3 MICROSECOND AS DATETIME)) AS newdate