How to get hours and minutes by subtracting two dates? - sql

I want to get the hours and minutes between two dates. But it's not working and I getting this error
ORA-01843: not a valid month
01843. 00000 - "not a valid month"
Here's the script
select q.name as queue_name
,to_date(t.create_time, 'dd-mon-yyyy hh24:mi:ss')
,to_date(t.close_time, 'dd-mon-yyyy hh24:mi:ss')
,NUMTOYMINTERVAL(t.close_time - t.create_time, 'DAY')*24
,NUMTOYMINTERVAL(t.close_time - t.create_time, 'DAY')*24*60
from app_account.otrs_ticket t
left join app_account.otrs_user u
on t.create_user_id=u.id
left join app_account.otrs_queue q
on q.id=t.queue_id
group by to_date(t.create_time, 'dd-mon-yyyy hh24:mi:ss')
,to_date(t.close_time, 'dd-mon-yyyy hh24:mi:ss')
,NUMTOYMINTERVAL(t.close_time - t.create_time, 'DAY')*24
,NUMTOYMINTERVAL(t.close_time - t.create_time, 'DAY')*24*60
,q.name
order by to_date(t.create_time, 'dd-mon-yyyy hh24:mi:ss') desc;

Try this you will get atleast idea how to calculate time difference between two dates.
SELECT floor(((date1-date2)*24*60*60)/3600)
|| ' HOURS ' ||
floor((((date1-date2)*24*60*60) -
floor(((date1-date2)*24*60*60)/3600)*3600)/60)
|| ' MINUTES ' ||
round((((date1-date2)*24*60*60) -
floor(((date1-date2)*24*60*60)/3600)*3600 -
(floor((((date1-date2)*24*60*60) -
floor(((date1-date2)*24*60*60)/3600)*3600)/60)*60) ))
|| ' SECS ' time_difference
FROM dates;
TIME_DIFFERENCE
--------------------------------------------------------------------------------
24 HOURS 0 MINUTES 0 SECS
1 HOURS 0 MINUTES 0 SECS
0 HOURS 1 MINUTES 0 SECS
For Futher Reference Please Refer This Link :
Click Here

use NUMTODSINTERVAL(close_date - create_date, 'DAY') or NUMTOYMINTERVAL(close_date - create_date, 'DAY') for calculating the difference in days.
For hours multiply the value with 24 and for minutes with 24*60.
select q.name as queue_name
,to_date(t.create_time, 'dd-mon-yyyy hh24:mi:ss') as create_date
,to_date(t.close_time, 'dd-mon-yyyy hh24:mi:ss') as close_date
,NUMTODSINTERVAL(close_date - create_date, 'DAY') or NUMTOYMINTERVAL(close_date - create_date, 'DAY')*24
,NUMTODSINTERVAL(close_date - create_date, 'DAY') or NUMTOYMINTERVAL(close_date - create_date, 'DAY') *24*60
from app_account.otrs_ticket t
left join app_account.otrs_user u
on t.create_user_id=u.id
left join app_account.otrs_queue q
on q.id=t.queue_id
where q.name not like 'Facilities Management::%'
and q.name not like 'HR::%'
and q.name not like 'Raw%'
and q.name not like 'Procurement::%'
and q.name not like 'Facilities Management%'
and q.name not like 'Junk%'
and q.name not like 'Facility Request Test%'
and q.name not like 'Misc%'
and q.name not like 'POS::POS issue - need paper%'
group by to_date(t.create_time, 'dd-mon-yyyy hh24:mi:ss') as create_date
,to_date(t.close_time, 'dd-mon-yyyy hh24:mi:ss') as close_date
order by to_date(t.create_time, 'dd-mon-yyyy hh24:mi:ss') desc;

Another way would be to calculate the values directly without using any oracle function:
Here is an example:
WITH q AS
(SELECT TO_DATE(create_time, 'YYYY-MM-DD HH24:MI') AS d_start,
TO_DATE(close_time, 'YYYY-MM-DD HH24:MI') AS d_end
FROM app_account.otrs_ticket
)
SELECT FLOOR((d_end - d_start) * 24) AS hours,
FLOOR(((d_end - d_start) * 24 * 60) - (floor((d_end - d_start) * 24) * 60)) AS minutes
FROM q;
Hope it helps!

Cast your DATE (or whatever data type it is) to a TIMESTAMP. The difference gives you an INTERVAL value where you can extract all information. Should be like this:
EXTRACT(DAY FROM CAST(t.close_time AS TIMESTAMP) - CAST(t.create_time AS TIMESTAMP))*24*60
+ EXTRACT(HOUR FROM CAST(t.close_time AS TIMESTAMP) - CAST(t.create_time AS TIMESTAMP))*60
+ EXTRACT(MINUTE FROM CAST(t.close_time AS TIMESTAMP) - CAST(t.create_time AS TIMESTAMP)) AS MINUTES

Related

Oracle SQL time difference in HH:MM:SS

I am trying to get time difference in Oracle database. The time difference for all the rows is okay except for one row. Here is the query
SELECT MAX(REGEXP_SUBSTR (CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls
The time difference occurs with the following row:
call_end: '2020-02-20 13:00:20'
call_start: '2020-02-20 12:56:03'
The returned result is '11:55:43' which is wrong. As the correct answer should be '00:04:17'
It looks like your table already stores the call start/end times as dates, and you're doing an implicit conversion from date to string, then converting back to a date. You can see that result if you have HH instead of HH24 in your NLS_DATE_FORMAT setting:
alter session set nls_date_format = 'YYYY-MM-DD HH:MI:SS';
with calls (call_end, call_start) as (
select cast(timestamp '2020-02-20 13:00:20' as date), cast(timestamp '2020-02-20 12:56:03' as date) from dual
)
SELECT MAX(REGEXP_SUBSTR (CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls;
CALL_TIME
---------------------------
11:55:43
When you do
TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS')
because it's already a date you're really doing:
TO_DATE(TO_CHAR(call_end, <NLS_DATE_FORMAT>), 'YYYY/MM/DD HH24:MI:SS')
so with my setting (and yours must be similar, maybe with slashes instead of dashes) that is:
TO_DATE(TO_CHAR(call_end, 'YYYY-MM-DD HH:MI:SS'), 'YYYY/MM/DD HH24:MI:SS')
and the mismatch between HH and HH24 becomes more obvious. So, you're actually converting the strings 2020-02-20 01:00:20 and 2020-02-20 12:56:03 back to dates, and the time difference between 01:00:20 and 12:56:03 is 11:55:43. Well - actually, it is minus 11 hours:
SELECT CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP)
FROM calls;
CAST(TO_DATE(CALL_E
-------------------
-00 11:55:43.000000
but your regex isn't picking that up.
As they are dates skip that part of the conversion completely, and just cast directly to timestamps if you want intervals to work from:
SELECT MAX(REGEXP_SUBSTR (CAST(call_end AS TIMESTAMP) - CAST(call_start AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls;
CALL_TIME
---------------------------
00:04:17
or use a substring instead of a regex, as #MTO showed.
You could also leave them as dates, get the difference as a fraction of a day, add that back to any nominal date at midnight, and then convert the resulting date to a string:
SELECT TO_CHAR(date '2000-01-01' + MAX(call_end - call_start), 'HH24:MI:SS') AS call_time
FROM calls;
CALL_TIM
--------
00:04:17
This won't work properly for a call that lasts more than 24 hours (used to see that a lot with modem calls, but can still happen); but neither will your interval approach. Both ignore any full days and just show the remainder. There are ways to handle that of course, but you'd need to decide how you want to display it - with separate day count (like interval does anyway), or with the 'hours' number allowed to go above 24... but then you may go above 99 hours...
Your question shows the call end time as 2020-02-20 13:00:20, which suggests that's how your client is displaying it when you query the table. Some clients (I think PL/SQL Developer, but not sure, it's been a while) use their own preferences/settings instead of honouring the session's NLS settings. But that has no effect on how Oracle behaves internally when it has to do implicit conversions.
I'm not sure if you want the result as an interval or timestamp, but this should do what you want:
select t.*, call_end - call_start,
substr(to_char(call_end - call_start, 'HH24:MI:SS'), 12, 8) as str from (select timestamp '2020-02-20 13:00:20.000' as call_end,
timestamp '2020-02-20 12:56:03.000' as call_start
from dual) t
Here is a db<>fiddle.
Assuming your data types are strings (you really should store them as DATE data types), you can use:
SELECT SUBSTR(
MAX(
( TO_DATE( call_end, 'YYYY-MM-DD HH24:MI:SS' )
- TO_DATE( call_start, 'YYYY-MM-DD HH24:MI:SS' )
) DAY(1) TO SECOND
),
4,
8
) AS call_time
FROM calls
If they are already DATE data types then, in the MAX aggregation, just use:
( call_end - call_start ) DAY(1) TO SECOND
So for your data:
CREATE TABLE calls ( call_end, call_start ) AS
SELECT '2020-02-20 13:00:20', '2020-02-20 12:56:03' FROM DUAL
This outputs:
| CALL_TIME |
| :-------- |
| 00:04:17 |
db<>fiddle here
Mr Gyl, You can as well do: db <> fiddle :This solution will also consider when the call has lapsed over days by adding those hours together:
WITH da AS (
SELECT
NUMTODSINTERVAL(TO_DATE('2020-02-20 13:00:20', 'yyyy-mm-dd hh24:mi:ss') - TO_DATE('2020-02-20 12:56:03', 'yyyy-mm-dd hh24:mi:ss'), 'DAY') AS call_diff
FROM
dual ) SELECT
EXTRACT( DAY FROM call_diff )*24 + EXTRACT( HOUR FROM call_diff )|| ':' || EXTRACT( MINUTE FROM call_diff ) || ':' || EXTRACT( SECOND FROM call_diff ) DIFFERENCE
FROM
da
DIFFERENCE|
----------|
0:4:17 |
So you can implement as
WITH da AS (
SELECT
NUMTODSINTERVAL(TO_DATE(call_end, 'yyyy-mm-dd hh24:mi:ss') - TO_DATE(call_start, 'yyyy-mm-dd hh24:mi:ss'), 'DAY') AS call_diff
FROM
calls
) SELECT
EXTRACT( DAY FROM call_diff )*24 + EXTRACT( HOUR FROM call_diff )|| ':' || EXTRACT( MINUTE FROM call_diff ) || ':' || EXTRACT( SECOND FROM call_diff ) TIMESTAMP
FROM
da
Please see below response but this will only work within 24hrs and returns both '00'
WITH sample_lt AS(
SELECT '2020-02-20 12:56:03' START_TIME, '2020-02-20 13:00:20' END_TIME FROM dual
)
SELECT start_time,
end_time,
TO_CHAR (TRUNC (SYSDATE) + (to_date(end_time, 'yyyy-mm-dd HH24:MI:SS') -
to_date(start_time, 'yyyy-mm-dd HH24:MI:SS')
) , 'hh24:mi:ss' ) duration
FROM sample_lt ;

Retrieve only records with today's date from an epoc timestamped column

I have a table which has 'completed' column with the timestamp but the timestamp is in epoch format. I am using the below query to translate the epoch time to a normal date but I am not sure how to filter the records that were created today. Hope someone can assist.
Here is the query I am using:
select name, status, message, completion_status,
TO_CHAR( FROM_TZ( CAST(DATE '1970-01-01' + (1/24/60/60/1000) * completed AS TIMESTAMP), 'America/New_York'), 'yyyy-mm-dd HH24:MI:SS')
as completed from Result;
Found it:
select name, status, message, completion_status,
TO_CHAR( FROM_TZ( CAST(DATE '1970-01-01' + (1/24/60/60/1000) * completed AS TIMESTAMP), 'America/New_York'), 'yyyy-mm-dd HH24:MI:SS')
as completed from Result
where TO_CHAR( FROM_TZ( CAST(DATE '1970-01-01' + (1/24/60/60/1000) * completed AS TIMESTAMP), 'America/New_York'), 'yyyy-mm-dd')
LIKE (SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD') as today_date FROM dual);

Subtract Dates in Oracle

I run this query in oracle:
select TO_CHAR(parsed_on, 'DD-MON-YYYY HH24:MI:SS'), TO_CHAR(creation_date, 'DD-MON-YYYY HH24:MI:SS'),
(parsed_on - creation_date) * 1000
from hotels
where TO_CHAR(CREATION_DATE, 'dd/mm/yyyy') = TO_CHAR(SYSDATE, 'dd/mm/yyyy') and incoming_psd_id = 608423671;
and this is the result, where I was excepting 12 seconds instead of 8.4
10-NOV-2017 07:49:54 10-NOV-2017 07:37:46 8,42592592592
The correct expression for seconds in Oracle would be:
(parsed_on - creation_date) * 24*60*60
The difference is decimal days.
If you want minutes, then use:
(parsed_on - creation_date) * 24*60
select ( to_date('10-NOV-2017 07:49:54', 'DD-MON-YYYY HH:MI:SS') - to_date('10-NOV-2017 07:37:46', 'DD-MON-YYYY HH:MI:SS') )* 86400
from dual
Correctly gives 728 seconds.
And if you want to compare dates, you have this option:
where trunc(CREATION_DATE) = trunc(SYSDATE)
TRUNC cuts out the time portion of dates by default, whereas trunc(SYSDATE, YYYY) would cut it down to years only.

ORACLE get count time range from both date

I'm using ORACLE, how to get time duration from this below date:
SELECT 24 * (TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI') - TO_CHAR(LASTACTIVITY, 'YYYY-MM-DD HH24:MI')) diff_hours
FROM WA_GA_TBL_USERS
I got error:
ORA-01722: invalid number
What I want is to get count time range from both date.
Example:
SYSDATE = 2017-10-06 17:00
LASTACTIVITY = 2017-10-06 15:30
And the result is: 1.5
And for next I will validate it with PHP
if($timeduration > 1) //1 means 1 hour
{
}
You already have date expressions, you don't need to convert them to varchars:
SELECT 24 * (SYSDATE - LASTACTIVITY) diff_hours FROM WA_GA_TBL_USERS
select
24 * (to_date('2017-10-06 17:00', 'YYYY-MM-DD hh24:mi') - to_date('2017-10-06 15:30', 'YYYY-MM-DD hh24:mi')) as diff_hours
from dual;
Usually i use php with mysql but i don't think the semantic is different the , i didn't try the code below but can be an help for you :
<?php
$c = oci_pconnect(...);
$s = oci_parse($c, "select 24 * (to_date('2017-10-06 17:00', 'YYYY-MM-DD hh24:mi') - to_date('2017-10-06 15:30', 'YYYY-MM-DD hh24:mi')) as diff_hours from dual");
oci_execute($s);
$res=oci_fetch_array($s);
if($res >1)
//..
?>

Oracle Set operator query

Have a SQL query on Oracle 11g which returns the count of whether a record having certain ID and status exists within +/- 15 minutes range in a table.
Now I wish to ignore the current date by adding a condition like AND TIMESTAMP < trunc(sysdate).
However, for cases where the record exists in todays date I wish to ignore the date comparison check in the query '2010-07-20 19:15:11' >= TO_CHAR(TIMESTAMP - (1/1440*15), 'YYYY-MM-DD HH24:MI:SS')
AND '2010-07-20 19:15:11' <= (TO_CHAR(TIMESTAMP + (1/1440*15), 'YYYY-MM-DD HH24:MI:SS'))
SELECT count(1) AS COUNT
FROM MASTER_ONE
WHERE ID='123' AND STATUS= 'ACTIVE'
AND '2010-07-20 19:15:11' >= TO_CHAR(TIMESTAMP - (1/1440*15), 'YYYY-MM-DD HH24:MI:SS')
AND '2010-07-20 19:15:11' <= (TO_CHAR(TIMESTAMP + (1/1440*15), 'YYYY-MM-DD HH24:MI:SS'))
UNION ALL
SELECT count(1) AS COUNT
FROM MASTER_TWO
WHERE ID='321' AND STATUS= 'ACTIVE'
AND '2010-07-20 19:15:11' >= TO_CHAR(TIMESTAMP - (1/1440*15), 'YYYY-MM-DD HH24:MI:SS')
AND '2010-07-20 19:15:11' <= (TO_CHAR(TIMESTAMP + (1/1440*15), 'YYYY-MM-DD HH24:MI:SS'))
How do I do this?
The first problem with your query is that you're doing a string comparison on the date. Use to_date instead of to_char and let Oracle help you out.
SELECT
to_date('2010-07-20 19:15:11', 'YYYY-MM-DD HH24:MI:SS') AS orig_date
, to_date('2010-07-20 19:15:11', 'YYYY-MM-DD HH24:MI:SS') - 1 / 24 / 4 AS fifteen_min_prior
, to_date('2010-07-20 19:15:11', 'YYYY-MM-DD HH24:MI:SS') + 1 / 24 / 4 AS fifteen_min_after
FROM dual;
Output:
ORIG_DATE FIFTEEN_MIN_PRIOR FIFTEEN_MIN_AFTER
------------------------- ------------------------- -------------------------
20-JUL-10 07:15:11 PM 20-JUL-10 07:00:11 PM 20-JUL-10 07:30:11 PM
Then use can use those dates in a BETWEEN condition in the predicate. See Oracle date "Between" Query.
I'm not quite clear what you mean by "However, for cases where the record exists in todays date I wish to ignore the date comparison check in the query." You'd just written that you want to exclude values from the current day. Either you're excluding today's records or you're not.
Ok, you can try something like this, if I understood you correctly:
SELECT count(1) AS COUNT
FROM MASTER_ONE
WHERE ID='123' AND STATUS= 'ACTIVE'
AND (timestamp > trunc(sysdate)
OR (timestamp < trunc(sysdate)
AND timestamp BETWEEN to_date(:yourInputDate,'DD/MM/YYYY HH24:MI:SS') - (1/1440*15)
AND to_date(:yourInputDate,'DD/MM/YYYY HH24:MI:SS') + (1/1440*15)))
UNION ALL
SELECT count(1) AS COUNT
FROM MASTER_TWO
WHERE ID='321' AND STATUS= 'ACTIVE'
AND (timestamp > trunc(sysdate)
OR (timestamp < trunc(sysdate)
AND timestamp BETWEEN to_date(:yourInputDate,'DD/MM/YYYY HH24:MI:SS') - (1/1440*15)
AND to_date(:yourInputDate,'DD/MM/YYYY HH24:MI:SS') + (1/1440*15)))
In this Select, you only apply the 15 minutes condition if your timestamp column has a date prior to sysdate.