Subtracting two dates to get hh:mm:ss - oracle

I am trying to subtract the END of an Event -START of an Event and get the time difference. I started by trying:
TO_CHAR(WXN_MOPACTIVITY.MOPEND, 'YYYY-MM-DD HH24:MI:SS') - TO_CHAR(WXN_MOPACTIVITY.MOPSTART, 'YYYY-MM-DD HH24:MI:SS') AS TIME_DIF
However, this doesn't work. I then tried:
(WXN_MOPACTIVITY.MOPEND - WXN_MOPACTIVITY.MOPSTART) AS TIME_DIF
This gives me answers like 0.125. How do I subtract these date fields and end up with a hh:mm:ss format?

Assuming that mopend and mopstart are both date columns, subtracting two dates return a difference in days. If you want to format that into hours, minutes, and seconds, you'll need to do a bit of math.
with diffs as (
select 0.125 diff_in_days from dual
)
select trunc( mod(diff_in_days * 24, 24) ) diff_in_hours,
trunc( mod(diff_in_days * 24 * 60, 60) ) diff_in_mins,
trunc( mod(diff_in_days * 24 * 60 * 60, 60) ) diff_in_secs
from diffs;
An alternative would be to use an interval and extract the individual components
with diffs as (
select numtodsinterval( 0.125, 'day' ) diff_interval from dual
)
select extract( hour from diff_interval ) diff_in_hours,
extract( minute from diff_interval ) diff_in_mins,
extract( second from diff_interval ) diff_in_secs
from diffs;

You can try
TO_DATE((WXN_MOPACTIVITY.MOPEND - WXN_MOPACTIVITY.MOPSTART),
'YYYY-MM-DD HH24:MI:SS') AS TIME_DIF
This should work. (I don't have any oracle DB right now)

Related

Oracle adding randim amount if seconds to a date

I have a date that is always set to midnight i.e. '07312021 00:00:00' how can I use dbms_random.value to add (1 second, 23:59:59) to that date.
SELECT DATE '2021-07-31' + INTERVAL '1' SECOND * FLOOR(DBMS_RANDOM.VALUE(0, 86400))
FROM DUAL;
Or
SELECT DATE '2021-07-31' + NUMTODSINTERVAL(
FLOOR(DBMS_RANDOM.VALUE(0, 86400)),
'SECOND'
)
FROM DUAL;
Or
SELECT DATE '2021-07-31' + DBMS_RANDOM.VALUE
FROM DUAL;
sqlfiddle here

How to get the date difference between start date and end date in oracle as hours and minutes

I have a scenario in which for example,my start_date ='12-SEP-2018 00:01:00' and End_date ='13-SEP-2018 14:55:00' . The difference between the 2 dates must be found out in Hours and minutes like'12:20'. This must be achieved in oracle database. I tried using the following logic :
SELECT
24 * (to_date('2009-07-07 22:00', 'YYYY-MM-DD hh24:mi') - to_date(
'2009-07-07 19:30', 'YYYY-MM-DD hh24:mi')) diff_hours
FROM
dual;
I was able to get the hour difference but unable to get minutes along with it.
CREATE TABLE table_name ( start_date DATE, end_date DATE );
INSERT INTO table_name VALUES ( TIMESTAMP '2009-07-07 19:30:00', TIMESTAMP '2009-07-07 22:00:00' );
Then you can subtract one from the other and cast it to a DAY TO SECOND interval and then just EXTRACT the component parts of the time:
SELECT EXTRACT( DAY FROM difference ) AS days,
EXTRACT( HOUR FROM difference ) AS hours,
EXTRACT( MINUTE FROM difference ) AS minutes,
EXTRACT( SECOND FROM difference ) AS seconds
FROM (
SELECT ( end_date - start_date ) DAY TO SECOND AS difference
FROM table_name
);
Outputs:
DAYS | HOURS | MINUTES | SECONDS
---: | ----: | ------: | ------:
0 | 2 | 30 | 0
or you can use arithmetic to calculate the values:
SELECT TRUNC( 24 * ( end_date - start_date ) ) AS hours,
TRUNC( MOD( 24 * 60 * ( end_date - start_date ), 60 ) ) AS minutes,
ROUND( MOD( 24 * 60 * 60 * ( end_date - start_date ), 60 ) ) AS seconds
FROM table_name;
which outputs:
HOURS | MINUTES | SECONDS
----: | ------: | ------:
2 | 30 | 0
db<>fiddle here
Since you want a string value, an alternative based on your query attempt is to add the difference between your two date values (which is a numeric value, the number of days between them, including fractional days) to an arbitrary fixed date; and then convert the result of that to a string:
SELECT to_char(date '0001-01-01'
+ (to_date('2009-07-07 22:00', 'YYYY-MM-DD hh24:mi') - to_date( '2009-07-07 19:30', 'YYYY-MM-DD hh24:mi')),
'HH24:MI') as diff
FROM dual;
DIFF
-----
02:30
If the difference can exceed 24 hours then you need to decide how to report that; if you want to include days as a separate figure then you can still use this approach, but need to subtract one (if your fixed date is the first) from the difference before formatting as a string:
SELECT to_char(date '0001-01-01'
+ (to_date('2009-07-08 22:00', 'YYYY-MM-DD hh24:mi') - to_date( '2009-07-07 19:30', 'YYYY-MM-DD hh24:mi'))
- 1,
'DDD:HH24:MI') as diff
FROM dual;
DIFF
---------
001:02:30
If you want the 'hours' value to be higher instead - e.g. '26:30' in this example - then it gets rather more complicated; I see #MTO has added the 'arithmetic' approach already so I won't repeat that. But then might be better off going down the extract() route (which you should consider anyway as it's more flexible and elegant...)

Convert local datetime (with time zone) to a Unix timestamp in Oracle

I currently have a SQL query that returns the correct local DATETIME from a Unix TIMESTAMP column in our DB.
Here is an example using a specific TIMESTAMP of 1539961967000:
SELECT FROM_TZ(CAST(DATE '1970-01-01' + 1539961967000 * (1/24/60/60/1000) AS TIMESTAMP), 'UTC') AT TIME ZONE 'America/Denver' DATETIME
FROM dual;
which returns:
DATETIME
19-OCT-18 09.12.47.000000000 AM AMERICA/DENVER
I am having a hard time reversing this query to return a Unix TIMESTAMP starting with a local DATETIME.
Has anyone ever encountered this before?
You can convert your timestamp with timezone to UTC, and then subtract the epoch from that:
select timestamp '2018-10-19 09:12:47.0 AMERICA/DENVER'
- timestamp '1970-01-01 00:00:00.0 UTC' as diff
from dual;
which gives you an interval data type:
DIFF
----------------------
+17823 15:12:47.000000
You can then extract the elements from that, and multiply each element by an appropriate factor to convert it to milliseconds (i.e. for days, 60*60*24*1000); and then add them together:
select extract(day from diff) * 86400000
+ extract(hour from diff) * 3600000
+ extract(minute from diff) * 60000
+ extract(second from diff) * 1000 as unixtime
from (
select timestamp '2018-10-19 09:12:47.0 AMERICA/DENVER'
- timestamp '1970-01-01 00:00:00.0 UTC' as diff
from dual
);
UNIXTIME
--------------------
1539961967000
db<>fiddle
This preserves milliseconds too, if the starting timestamp has them (this converts from a 'Unix' time while preserving them):
select (timestamp '1970-01-01 00:00:00.0 UTC' + (1539961967567 * interval '0.001' second))
at time zone 'America/Denver' as denver_time
from dual;
DENVER_TIME
--------------------------------------------
2018-10-19 09:12:47.567000000 AMERICA/DENVER
then to convert back:
select extract(day from diff) * 86400000
+ extract(hour from diff) * 3600000
+ extract(minute from diff) * 60000
+ extract(second from diff) * 1000 as unixtime
from (
select timestamp '2018-10-19 09:12:47.567 AMERICA/DENVER'
- timestamp '1970-01-01 00:00:00.0 UTC' as diff
from dual
);
UNIXTIME
--------------------
1539961967567
db<>fiddle
If your starting timestamp has greater precision than that then you'll need to truncate (or round/floor/ceil/cast) to avoid having a non-integer result; this version just truncates the extracted milliseconds part:
select diff,
extract(day from diff) * 86400000
+ extract(hour from diff) * 3600000
+ extract(minute from diff) * 60000
+ trunc(extract(second from diff) * 1000) as unixtime
from (
select timestamp '2018-10-19 09:12:47.123456789 AMERICA/DENVER'
- timestamp '1970-01-01 00:00:00.0 UTC' as diff
from dual
);
DIFF UNIXTIME
------------------------- --------------------
+17823 15:12:47.123456789 1539961967123
Without that truncation (or equivalent) you'd end up with 1539961967123.456789.
I'd forgotten about the leap seconds discrepancy; if you need/want to handle that, see this answer.
The main issue is that Oracle has two ways (at least) to convert a number of seconds to an interval day-to-second - either with a function or with a simple arithmetic operation on an interval literal - but no direct way to do the reverse.
In the two queries below, first I show how to convert a UNIX timestamp (in milliseconds since the Epoch) to an Oracle timestamp, without losing milliseconds. (See my comment under your Question, where I point out that your method will lose milliseconds.) Then I show how to reverse the process.
Like you, I ignore the difference between "timestamp at UTC" and "Unix timestamp" caused by "Unix timestamp" ignoring leap seconds. Your business must determine whether that is important.
Unix timestamp to Oracle timestamp with time zone (preserving milliseconds):
with
inputs (unix_timestamp) as (
select 1539961967186 from dual
)
select from_tz(timestamp '1970-01-01 00:00:00'
+ interval '1' second * (unix_timestamp/1000), 'UTC')
at time zone 'America/Denver' as oracle_ts_with_timezone
from inputs
;
ORACLE_TS_WITH_TIMEZONE
--------------------------------------
2018-10-19 09:12:47.186 America/Denver
Oracle timestamp with time zone to Unix timestamp (preserving milliseconds):
with
sample_data (oracle_ts_with_timezone) as (
select to_timestamp_tz('2018-10-19 09:12:47.186 America/Denver',
'yyyy-mm-dd hh24:mi:ss.ff tzr') from dual
)
select ( extract(second from ts)
+ (trunc(ts, 'mi') - date '1970-01-01') * (24 * 60 * 60)
) * 1000 as unix_timestamp
from ( select cast(oracle_ts_with_timezone at time zone 'UTC'
as timestamp) as ts
from sample_data
)
;
UNIX_TIMESTAMP
----------------
1539961967186

oracle convert unix epoch time to date

The context is that there is an existing application in our product which generates and sends the EPOCH number to an existing oracle procedure & vice versa. It works in that procedure using something like this
SELECT UTC_TO_DATE (1463533832) FROM DUAL
SELECT date_to_utc(creation_date) FROM mytable
When I tried these queries it does work for me as well with Oracle 10g server (and oracle sql developer 4.x if that matters).
In the existing procedure the requirement was to save the value as date itself (time component was irrelevant), however in the new requirement I have to convert unix EPOCH value to datetime (at the hours/mins/seconds level, or better in a specific format such as dd-MMM-yyyy hh:mm:ss) in an oracle query. Strangely I am unable to find any documentation around the UTC_TO_DATE and DATE_TO_UTC functions with Google. I have looked around at all different questions on stackoverflow, but most of them are specific to programming languages such as php, java etc.
Bottom line, how to convert EPOCH to that level of time using these functions (or any other functions) in Oracle query? Additionally are those functions I am referring could be custom or specific somewhere, as I don't see any documentation or reference to this.
To convert from milliseconds from epoch (assume epoch is Jan 1st 1970):
select to_date('19700101', 'YYYYMMDD') + ( 1 / 24 / 60 / 60 / 1000) * 1322629200000
from dual;
11/30/2011 5:00:00 AM
To convert that date back to milliseconds:
select (to_date('11/30/2011 05:00:00', 'MM/DD/YYYY HH24:MI:SS') - to_date('19700101', 'YYYYMMDD')) * 24 * 60 * 60 * 1000
from dual;
1322629200000
If its seconds instead of milliseconds, just omit the 1000 part of the equation:
select to_date('19700101', 'YYYYMMDD') + ( 1 / 24 / 60 / 60 ) * 1322629200
from dual;
select (to_date('11/30/2011 05:00:00', 'MM/DD/YYYY HH24:MI:SS') - to_date('19700101', 'YYYYMMDD')) * 24 * 60 * 60
from dual;
Hope that helps.
Another option is to use an interval type:
SELECT TO_TIMESTAMP('1970-01-01 00:00:00.0'
,'YYYY-MM-DD HH24:MI:SS.FF'
) + NUMTODSINTERVAL(1493963084212/1000, 'SECOND')
FROM dual;
It has this advantage that milliseconds won't be cut.
If your epoch time is stored as an integer.....
And you desire the conversion to Oracle date format.
Step 1-->
Add your epoch date (1462086000) to standard 01-jan-1970. 86400 is seconds in a 24 hour period.
*Select TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 from dual*
**output is 5/1/2016 7:00:00 AM**
Step 2--> Convert it to a CHAR . This is needed for formatting before additional functions can be applied.
*Select TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss') from dual*
output is 2016-05-01 07:00:00
Step 3--> Now onto Timestamp conversion
Select to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss') from dual
output is 5/1/2016 7:00:00.000000000 AM
Step 4--> Now need the TimeZone, usage of UTC
Select from_tz(to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss'),'UTC') from dual
output is 5/1/2016 7:00:00.000000000 AM +00:00
Step 5--> If your timezone need is PST
Select from_tz(to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss'),'UTC') at time zone 'America/Los_Angeles' TZ from dual
output is 5/1/2016 12:00:00.000000000 AM -07:00
Step 6--> Format the PST Timezone timestamp.
Select to_Char(from_tz(to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss'),'UTC') at time zone 'America/Los_Angeles' ,'DD-MON-YYYY HH24:MI:SS') TZ from dual
output is 01-MAY-2016 00:00:00
Step 7--> And finally, if your column is date datatype
Add to_DATE to the whole above Select.
Here it is for both UTC/GMT and EST;
GMT select (to_date('1970-01-01 00','yyyy-mm-dd hh24') +
(1519232926891)/1000/60/60/24) from dual;
EST select new_time(to_date('1970-01-01 00','yyyy-mm-dd hh24') +
(1519232926891)/1000/60/60/24, 'GMT', 'EST') from dual;
I thought somebody would be interested in seeing an Oracle function version of this:
CREATE OR REPLACE FUNCTION unix_to_date(unix_sec NUMBER)
RETURN date
IS
ret_date DATE;
BEGIN
ret_date:=TO_DATE('19700101','YYYYMMDD')+( 1/ 24/ 60/ 60)*unix_sec;
RETURN ret_date;
END;
/
I had a bunch of records I needed dates for so I updated my table with:
update bobfirst set entered=unix_to_date(1500000000+a);
where a is a number between 1 and 10,000,000.
A shorter method to convert timestamp to nanoseconds.
SELECT (EXTRACT(DAY FROM (
SYSTIMESTAMP --Replace line with desired timestamp --Maximum value: TIMESTAMP '3871-04-29 10:39:59.999999999 UTC'
- TIMESTAMP '1970-01-01 00:00:00 UTC') * 24 * 60) * 60 + EXTRACT(SECOND FROM
SYSTIMESTAMP --Replace line with desired timestamp
)) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to timestamp.
SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' + numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS TIMESTAMP FROM dual;
TIMESTAMP
26/08/20 09:33:47,263027000 UTC
As expected, above methods' results are not affected by time zones.
A shorter method to convert interval to nanoseconds.
SELECT (EXTRACT(DAY FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval --Maximum value: INTERVAL '+694444 10:39:59.999999999' DAY(6) TO SECOND(9) or up to 3871 year
) * 24 * 60) * 60 + EXTRACT(SECOND FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval
))) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to interval.
SELECT numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS INTERVAL FROM dual;
INTERVAL
+18500 09:33:47.263027
As expected, millis, micros and nanos are converted and reverted, dispite of SYSTIMESTAMP doesn't have nanosecounds information.
Replace 1000000000 by 1000, for example, if you'd like to work with milliseconds instead of nanoseconds.
I've tried some of posted methods, but almost of them are affected by the time zone or result on data loss after revertion, so I've decided do post the methods that works for me.

DateTime on Where Clause Oracle

it seems there are lot of query syntax to fetch data on oracle database, here I just want to ask about the query that works fine but I cant understand at all. The query is :
Select
....
From
...
Where
TO_CHAR(TO_DATE('01/01/1970 00:00:00', 'MM/DD/YYYY HH24:MI:SS') +
(create_date / ( 60 * 60 * 24 )),
'MM/DD/YY HH24:MI:SS') = '06/30/14 21:41:11'
;
From the query above it's work fine. But I cant understand why there's TO_DATE('01/01/1970 00:00:00', 'MM/DD/YYYY HH24:MI:SS and (create_date / ( 60 * 60 * 24 )),
'MM/DD/YY HH24:MI:SS')
on the create_date fields it show unix datetime such as 1404164471
Can anybody explain about this?
thanks in advance
TO_DATE('01/01/1970 00:00:00', 'MM/DD/YYYY HH24:MI:SS') converts a string (first argument) in certain format (second argument) to a date.
(create_date / ( 60 * 60 * 24 )) create_date contains seconds, this expression converts them into the number of days (1 minute = 60 seconds, 1 hour = 60 minutes, 1 day = 24 hours => 60*60*24 = the number of seconds in a day). When you add a number to a date Oracle thinks that this number contains days that's why you need such a conversation.
TO_DATE('01/01/1970 00:00:00', 'MM/DD/YYYY HH24:MI:SS') + (create_date / ( 60 * 60 * 24 )) gives you a date stored in create_date but in "traditional" format
It seems you need to compare unix time with date. It would be better to use this condition:
Select
....
From
...
Where create_date = trunc( (TO_DATE('06/30/14 21:41:11', 'MM/DD/YY HH24:MI:SS')
- TO_DATE('01/01/1970 00:00:00', 'MM/DD/YYYY HH24:MI:SS')
) * 24 * 60 * 60
);
The outer to_char(,) creates a string from the calculated date. This is so that it can be compared with the string '06/30/14 21:41:11'.
Inside the to_char some calculation is going on, the addition of to date values.
TO_DATE('01/01/1970 00:00:00', 'MM/DD/YYYY HH24:MI:SS')
+ (create_date / ( 60 * 60 * 24 ))
The to_date function takes a date value in string format ('01/01/1970 00:00:00'), and a format string ('MM/DD/YYYY HH24:MI:SS') to tell it how to interpret the date value.
create_date seems to be a number in seconds 9probably since 01-01-1970). It is devided by the number of seconds in a day so that will result in a number of days. So what you get is the create date in real calendar value.

Resources