Insert UTC timestamp (millis) into Oracle timestamp column - sql

Is there a way how to directly convert value in milliseconds (e.g. 1480515430991) to Oracle TIMESTAMP(6) column? Like some pattern I'm missing for the TO_TIMESTAMP or TO_DATE functions?
All I could find so far are some calculations with intervals and to_date('1970-01-01','YYYY-MM-DD') or other crazy "manual" calculations.
Thanks
EDIT:
Thanks guys. I didn't ask how to do the conversion though. I asked if there is a direct (native, more straightforward) way to achieve it and avoid these calculations for a given input. I am just a curious person and there are many undocumented features out there (Oracle not excluded). I guess NO is my answer then.

Correct function, i.e. include time zone consideration and milliseconds would be this one (using literals):
create or replace function epoch2timestamp(epoch number) return timestamp as
begin
return (TIMESTAMP '1970-01-01 00:00:00 UTC' + epoch/1000 * INTERVAL '1' SECOND) AT LOCAL;
end;
/

This is how to get timestamp from epoch.:
select to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch from dual;
So in insert please insert to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch instead of epoch. You can also create function for that:
create or replace function epoch2timestamp(epoch number) return timestamp as
begin
return to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch;
end;
/
And then operate on function. These are not "crazy manual calculations" just a legal way of conversion.

If you want to express the unix time, the time since epoch, you need seconds, not milliseconds. See Unixtime
Oracle's datatype TIMESTAMP is in fractional seconds, as you can read in the Oracle documentation. Link zu 11g documentation
Year, month, and day values of date, as well as hour, minute, and second values of time, where fractional_seconds_precision is the number of digits in the fractional part of the SECOND datetime field. Accepted values of fractional_seconds_precision are 0 to 9.
And to answer your question: there is a TO_TIMESTAMP function. See 11g documentation
The TO_TIMESTAMP function converts text data to a value of TIMESTAMP data type.
You can use it like this
TO_TIMESTAMP('2016/11/30 15:53:18', 'YYYY/MM/DD HH:MI:SS')
and would get '30-NOV-16 15.53.18.000000000 AM'
If, for some reason, you really need to display the seconds sind epoch, you can use the noncrazy calculation
select (SYSDATE - to_date('1970-01-01', 'yyyy-MM-dd')) * 24 * 60 * 60 from dual;

Related

Interval Date to days [duplicate]

I have two timestamp columns: arrTime and depTime.
I need to find the number of munites the bus is late.
I tried the following:
SELECT RouteDate, round((arrTime-depTime)*1440,2) time_difference
FROM ...
I get the following error: inconsistent datatype . expected number but got interval day to second
How can i parse the nuber of minutes?
If i simply subtract: SELECT RouteDate, arrTime-depTime)*1440 time_difference
The result is correct but not well formatted:
time_difference
+00000000 00:01:00 0000000
The result of timestamp arithmetic is an INTERVAL datatype. You have an INTERVAL DAY TO SECOND there...
If you want the number of minutes one way would be to use EXTRACT(), for instance:
select extract( minute from interval_difference )
+ extract( hour from interval_difference ) * 60
+ extract( day from interval_difference ) * 60 * 24
from ( select systimestamp - (systimestamp - 1) as interval_difference
from dual )
Alternatively you can use a trick with dates:
select sysdate + (interval_difference * 1440) - sysdate
from (select systimestamp - (systimestamp - 1) as interval_difference
from dual )
The "trick" version works because of the operator order of precedence and the differences between date and timestamp arithmetic.
Initially the operation looks like this:
date + ( interval * number ) - date
As mentioned in the documentation:
Oracle evaluates expressions inside parentheses before evaluating those outside.
So, the first operation performed it to multiply the interval by 1,440. An interval, i.e. a discrete period of time, multiplied by a number is another discrete period of time, see the documentation on datetime and interval arithmetic. So, the result of this operation is an interval, leaving us with:
date + interval - date
The plus operator takes precedence over the minus here. The reason for this could be that an interval minus a date is an invalid operation, but the documentation also implies that this is the case (doesn't come out and say it). So, the first operation performed is date + interval. A date plus an interval is a date. Leaving just
date - date
As per the documentation, this results in an integer representing the number of days. However, you multiplied the original interval by 1,440, so this now represented 1,440 times the amount of days it otherwise would have. You're then left with the number of seconds.
It's worth noting that:
When interval calculations return a datetime value, the result must be an actual datetime value or the database returns an error. For example, the next two statements return errors:
The "trick" method will fail, rarely but it will still fail. As ever it's best to do it properly.
SELECT (arrTime - depTime) * 1440 time_difference
FROM Schedule
WHERE ...
That will get you the time difference in minutes. Of course, you can do any rounding that you might need to to get whole minutes....
Casting to DATE first returns the difference as a number, at least with the version of Oracle I tried.
round((cast(arrTime as date) - cast(depTime as date))*1440)
You could use TO_CHAR then convert back to a number. I have never tested the performance compared to EXTRACT, but the statement works with two dates instead of an interval which fit my needs.
Seconds:
(to_char(arrTime,'J')-to_char(depTime,'J'))*86400+(to_char(arrTime,'SSSSS')-to_char(depTime,'SSSSS'))
Minutes:
round((to_char(arrTime,'J')-to_char(depTime,'J'))*1440+(to_char(arrTime,'SSSSS')-to_char(depTime,'SSSSS'))/60)
J is julian day and SSSSS is seconds in day. Together they give an absolute time in seconds.

Between two Unix dates in Oracle

Thanks to previous question...
I have a more simplified OR statement.
Question is instead of a IN how could I change this to a between?
TO_DATE(TO_CHAR(TO_DATE(''19700101'',''yyyymmdd'') + + (FLOOR(ph.change_date/24/60/60)))) IN (''23-DEC-2020'', ''29-DEC-2020'')
So I want to say between the 23-DEC-2020 and 29-DEC-2020 including both?
Thanks
If you want to use an index on the change_date column then perform the conversion on the literal values and convert them to epoch times (rather than converting the column's epoch time to a date, which would not allow you to use a normal index on the column):
ph.change_date BETWEEN ( DATE '2020-12-23' - DATE '1970-01-01' ) * 86400
AND ( DATE '2020-12-29' - DATE '1970-01-01' ) * 86400 + 86399
change_date seems to be in Unix timestamp format -- the number of seconds since 1970-01-01. I would recommend doing the comparison by converting constant values to the same format:
where ph.change_date >= (date '2020-12-23' - date '1970-01-01') * 24 * 60 * 60 and
ph.change_date < (date '2020-12-30' - date '1970-01-01') * 24 * 60 * 60
Note that this is index (and partition) friendly. And, the second comparison is < on the next day to get the entire day.
If you need to deal with the column as "real" dates, you can add a computed column
alter table t add column change_date_date date generated always as
(cast(date '1970-01-01' as timestamp) + change_date * interval '1' second);
You can then reference change_date_date and even define a an index on it.
You can do it with the between keyword.
For example:
to_date('2021.01.06', 'yyyy.mm.dd') between to_date('2021.01.01', 'yyyy.mm.dd') and to_date('2021.01.31', 'yyyy.mm.dd')
I think we can try like this
where <expression>
between TO_DATE('23-DEC-2020','DD-MON-YYYY') and
TO_DATE('29-DEC-2020','DD-MON-YYYY')
You can convert the epoch time(ph.change_date) to date and then compare as follows:
Date'1970-01-01' + FLOOR(ph.change_date/24/60/60) -- considering that change_date is epoch time
between date'2020-12-23' and date'2020-12-23'

PLSQL - convert unix timestamp with millsecond precision to timestamp(6)

I have a unix timstamp with millsecond precision like below:
1523572200000
I need to convert it to timestamp(6). This is the format I need:
05-NOV-14 09.45.00.000000000 AM
(Fyi examples above are not matching dates, just using as example.)
What's the best way to go about this?
Thanks!
The following might work for you (where myunixtimestamp is the name of the column in which your Unix timestamps are stored):
SELECT TIMESTAMP'1970-01-01 00:00:00.000' + NUMTODSINTERVAL(myunixtimestamp/1000, 'SECOND')
FROM mytable;
For example,
SELECT TIMESTAMP'1970-01-01 00:00:00.000' + NUMTODSINTERVAL(1523572200000/1000, 'SECOND')
FROM dual;
gives a result of 2018-04-12 10:30:00.000000000 PM.
Hope this helps.
Assuming that current timestamp is: 1523572200000, try following:
select cast (to_date('1970-01-01', 'YYYY-MM-DD') + 1523572200000/1000/60/60/24 as timestamp) from dual;
where:
to_date('1970-01-01', 'YYYY-MM-DD') is epoch time
<unix_timestamp>/60/60/24 was divided by 1000 miliseconds 60 second and 60 minutes and 24 hours because in oracle we are adding days

Oracle: subtract millisecond from a datetime

I thought it was really simple but it isn't.
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY')
- 1/(24*50*60*1000) data
FROM dual;
It simply doesn't work.
Other details:
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') -
NUMTODSINTERVAL(1/(24*50*60*1000),'HOUR') data
FROM dual;
doesn't work..
The right seems to be
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') -
NUMTODSINTERVAL(1/(24*25*60*1000),'HOUR') data
FROM dual;
Why? How does it work?
For adding or subtracting an amount of time expressed as a literal you can use INTERVAL.
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY')
- INTERVAL '0.001' SECOND
FROM dual;
As well there are now standard ways to express date and time literals and avoid the use of various database specific conversion functions.
SELECT TIMESTAMP '2012-10-08 00:00:00'
- INTERVAL '0.001' SECOND DATA
FROM dual;
For your original question the time part of a day is stored in fractional days. So one second is:
1 / (hours in day * minutes in hour * seconds in a minute)
Divide by 1000 to get milliseconds.
1 / (24 * 60 * 60 * 1000)
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') - NUMTODSINTERVAL(1/(24*50*60*1000),'HOUR') data
FROM dual;
OUTPUT
DATA
---------------------------------
09/AUG/12 11:59:59.999950000 PM
1 row selected.
The answer posted above subtracts a tenth of a millisecond from the date. I think what you want is the following:
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY')-NUMTODSINTERVAL(1/1000,'SECOND')
FROM dual;
Output:
DATA
---------------------------------------------------------------------------
09-AUG-12 11.59.59.999000000 PM
^^^
|||
tenths|thousandths
|
hundredths
The following NUMTODSINTERVAL(1/(24*25*60*1000),'HOUR') seems to work only because 24*25 = 600. But that number is wrong because 1/(600*60*1000) of an hour is a tenth of a millisecond, not a millisecond. If you want to use 'HOUR' in NUMTODSINTERVAL() you should use 1/(60*60*1000) (sixty minutes in an hour, sixty seconds in a minute, 1000 ms in a second).
This is correct (with a millisecond being 1000th of a second):-
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') - NUMTODSINTERVAL(1/1000,'SECOND') data FROM dual;
DATA
-----------------------------
09-AUG-12 23.59.59.999000000
As to why the other code isn't working it's because you aren't calculating a millisecond correctly. An hour must be divided by 60 to give minutes and again by 60 to given seconds then by 1000 to give a millisecond, thus if you must use HOUR as the interval then it is:-
SELECT TO_TIMESTAMP('10/08/2012','DD/MM/YYYY') - NUMTODSINTERVAL(1/(60*60*1000),'HOUR') as data FROM dual;
DATA
---------------------------------------------------------------------------
09-AUG-12 23.59.59.999000000
select TO_CHAR(TO_TIMESTAMP('10.05.2012', 'DD.MM.YYYY') -
NUMTODSINTERVAL(1/1000, 'SECOND'), 'DD.MM.YYYY HH24:MI:SS:FF3') Res
from dual;
RES
-----------------------------
09.05.2012 23:59:59.999

Display correct subtraction of two timestamps in create view

By using normal minus '-' function between two timestamps, the answer given from oracle is incorrect.
This is what i want to do:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH24:MI TZR';
Created table:
CREATE TABLE TEST (
StartTime timestamp with time zone
,EndTime timestamp with time zone
,Science varchar2(7)
);
I create the column data type as timestamp with time zone. This is value I have inserted:
INSERT INTO TEST
VALUES('05-OCT-2013 01:00 +08:00'
,'05-OCT-2013 23:00 +06:00'
,'SCIENCE');
INSERT INTO TEST
VALUES('05-OCT-2013 12:00 +08:00'
,'05-OCT-2013 15:00 -12:00'
,'Maths');
Attempted for rounding time:
CREATE VIEW TESTRECRDS AS
SELECT (Extract(hour FROM(ENDTIME- STARTTIME)) || 'Hours' ||
Extract(minute FROM(ENDTIME- STARTTIME))>=60 Then (Extract(hour FROM(ENDTIME- STARTTIME)) + Extract(minute FROM(ENDTIME- STARTTIME))/60 ELSE 0 END || 'Minutes' AS DURATION,
Science
FROM Test;
Now i have two questions regarding on the calculation and rounding off the minutes to nearest hours.
First let's say the endtime is 1535 +0600 and starttime is 01:50 +0800
So when i deduct endtime - starttime:
the formula should be:
2135 - 0950 = 2085 - 0950
= 1135
But if i use my successful attempt answer to calculate, it is not the correct exact answer. The oracle answer would be 15 hours 45 minutes.
In your last CREATE VIEW statement you try to multiply text, which cannot work:
SELECT To_Char(STARTTIME - ENDTIME, 'HH24:MI TZR')*24 AS DURATION
*24 is operating on the text to_char() returns.
You have to multiply the interval before converting to text.
You define the column Science varchar2(6), then you insert 'SCIENCE', a 7-letter word?
I also fixed a syntax error in your INSERT statement: missing '.
About your comment:
"I would like to insert timestamp with timezone during creation of my tables. Can DATE data type do that too?
Read about data types in the manual.
The data type date does not include time zone information.
If by "timezone difference" you mean the difference between the timezone modifiers, use this to calculate:
SELECT EXTRACT(timezone_hour FROM STARTTIME) AS tz_modifier FROM tbl
Keywords here are timezone_hour and is timezone_minute. Read more in the manual.
But be aware that these numbers depend on the daylight saving hours and such shenanigans. Very uncertain territory!
Get it in pretty format - example:
SELECT to_char((EXTRACT (timezone_hour FROM STARTTIME) * 60
+ EXTRACT (timezone_minutes FROM STARTTIME))
* interval '1 min', 'HH:MI')
In PostgreSQL you would have the simpler EXTRACT (timezone FROM STARTTIME), but I don't think Oracle supports that. Can't test now.
Here is a simple demo how you could round minutes to hours:
SELECT EXTRACT(hour FROM (ENDTIME - STARTTIME))
+ CASE WHEN EXTRACT(minute FROM (ENDTIME - STARTTIME)) >= 30 THEN 1 ELSE 0 END
FROM Test;
I'm not sure what number you're trying to calculate, but when you subtract two dates in Oracle, you get the difference between the dates in units of days, not a DATE datatype
SELECT TO_DATE('2011-01-01 09:00', 'yyyy-mm-dd hh24:mi') -
TO_DATE('2011-01-01 08:00', 'yyyy-mm-dd hh24:mi') AS diff
FROM dual
DIFF
----------
.041666667
In this case 8am and 9am are 0.41667 days apart. This is not a date object, this is a scalar number, so formatting it as HH24:MI doesn't make any sense.
To round you will need to do a bit of more math. Try something like:
TO_DATE(ROUND((ENDTIME - STARTTIME) * 96) / 96, 'HH24:MI')
The difference between dates is in days. Multiplying by 96 changes the measure to quarter hours. Round, then convert back to days, and format. It might be better to use a numeric format want to format, in which case you would divide by 4 instead of 96.
Timezone is not particularly relevant to a time difference. You will have to adjust the difference from UTC to that timezone to get the right result with Timezone included.