How do I calculate the time difference in milliseconds between two timestamps in Oracle?
When you subtract two variables of type TIMESTAMP, you get an INTERVAL DAY TO SECOND which includes a number of milliseconds and/or microseconds depending on the platform. If the database is running on Windows, systimestamp will generally have milliseconds. If the database is running on Unix, systimestamp will generally have microseconds.
1 select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' )
2* from dual
SQL> /
SYSTIMESTAMP-TO_TIMESTAMP('2012-07-23','YYYY-MM-DD')
---------------------------------------------------------------------------
+000000000 14:51:04.339000000
You can use the EXTRACT function to extract the individual elements of an INTERVAL DAY TO SECOND
SQL> ed
Wrote file afiedt.buf
1 select extract( day from diff ) days,
2 extract( hour from diff ) hours,
3 extract( minute from diff ) minutes,
4 extract( second from diff ) seconds
5 from (select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' ) diff
6* from dual)
SQL> /
DAYS HOURS MINUTES SECONDS
---------- ---------- ---------- ----------
0 14 55 37.936
You can then convert each of those components into milliseconds and add them up
SQL> ed
Wrote file afiedt.buf
1 select extract( day from diff )*24*60*60*1000 +
2 extract( hour from diff )*60*60*1000 +
3 extract( minute from diff )*60*1000 +
4 round(extract( second from diff )*1000) total_milliseconds
5 from (select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' ) diff
6* from dual)
SQL> /
TOTAL_MILLISECONDS
------------------
53831842
Normally, however, it is more useful to have either the INTERVAL DAY TO SECOND representation or to have separate columns for hours, minutes, seconds, etc. rather than computing the total number of milliseconds between two TIMESTAMP values.
Here's a stored proc to do it:
CREATE OR REPLACE function timestamp_diff(a timestamp, b timestamp) return number is
begin
return extract (day from (a-b))*24*60*60 +
extract (hour from (a-b))*60*60+
extract (minute from (a-b))*60+
extract (second from (a-b));
end;
/
Up Vote if you also wanted to beat the crap out of the Oracle developer who negated to his job!
BECAUSE comparing timestamps for the first time should take everyone an hour or so...
Easier solution:
SELECT numtodsinterval(date1-date2,'day') time_difference from dates;
For timestamps:
SELECT (extract(DAY FROM time2-time1)*24*60*60)+
(extract(HOUR FROM time2-time1)*60*60)+
(extract(MINUTE FROM time2-time1)*60)+
extract(SECOND FROM time2-time1)
into diff FROM dual;
RETURN diff;
Select date1 - (date2 - 1) * 24 * 60 *60 * 1000 from Table;
I know that this has been exhaustively answered, but I wanted to share my FUNCTION with everyone. It gives you the option to choose if you want your answer to be in days, hours, minutes, seconds, or milliseconds. You can modify it to fit your needs.
CREATE OR REPLACE FUNCTION Return_Elapsed_Time (start_ IN TIMESTAMP, end_ IN TIMESTAMP DEFAULT SYSTIMESTAMP, syntax_ IN NUMBER DEFAULT NULL) RETURN VARCHAR2 IS
FUNCTION Core (start_ IN TIMESTAMP, end_ IN TIMESTAMP DEFAULT SYSTIMESTAMP, syntax_ IN NUMBER DEFAULT NULL) RETURN VARCHAR2 IS
day_ VARCHAR2(7); /* This means this FUNCTION only supports up to 99 days */
hour_ VARCHAR2(9); /* This means this FUNCTION only supports up to 999 hours, which is over 41 days */
minute_ VARCHAR2(12); /* This means this FUNCTION only supports up to 9999 minutes, which is over 17 days */
second_ VARCHAR2(18); /* This means this FUNCTION only supports up to 999999 seconds, which is over 11 days */
msecond_ VARCHAR2(22); /* This means this FUNCTION only supports up to 999999999 milliseconds, which is over 11 days */
d1_ NUMBER;
h1_ NUMBER;
m1_ NUMBER;
s1_ NUMBER;
ms_ NUMBER;
/* If you choose 1, you only get seconds. If you choose 2, you get minutes and seconds etc. */
precision_ NUMBER; /* 0 => milliseconds; 1 => seconds; 2 => minutes; 3 => hours; 4 => days */
format_ VARCHAR2(2) := ', ';
return_ VARCHAR2(50);
BEGIN
IF (syntax_ IS NULL) THEN
precision_ := 0;
ELSE
IF (syntax_ = 0) THEN
precision_ := 0;
ELSIF (syntax_ = 1) THEN
precision_ := 1;
ELSIF (syntax_ = 2) THEN
precision_ := 2;
ELSIF (syntax_ = 3) THEN
precision_ := 3;
ELSIF (syntax_ = 4) THEN
precision_ := 4;
ELSE
precision_ := 0;
END IF;
END IF;
SELECT EXTRACT(DAY FROM (end_ - start_)) INTO d1_ FROM DUAL;
SELECT EXTRACT(HOUR FROM (end_ - start_)) INTO h1_ FROM DUAL;
SELECT EXTRACT(MINUTE FROM (end_ - start_)) INTO m1_ FROM DUAL;
SELECT EXTRACT(SECOND FROM (end_ - start_)) INTO s1_ FROM DUAL;
IF (precision_ = 4) THEN
IF (d1_ = 1) THEN
day_ := ' day';
ELSE
day_ := ' days';
END IF;
IF (h1_ = 1) THEN
hour_ := ' hour';
ELSE
hour_ := ' hours';
END IF;
IF (m1_ = 1) THEN
minute_ := ' minute';
ELSE
minute_ := ' minutes';
END IF;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := d1_ || day_ || format_ || h1_ || hour_ || format_ || m1_ || minute_ || format_ || s1_ || second_;
RETURN return_;
ELSIF (precision_ = 3) THEN
h1_ := (d1_ * 24) + h1_;
IF (h1_ = 1) THEN
hour_ := ' hour';
ELSE
hour_ := ' hours';
END IF;
IF (m1_ = 1) THEN
minute_ := ' minute';
ELSE
minute_ := ' minutes';
END IF;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := h1_ || hour_ || format_ || m1_ || minute_ || format_ || s1_ || second_;
RETURN return_;
ELSIF (precision_ = 2) THEN
m1_ := (((d1_ * 24) + h1_) * 60) + m1_;
IF (m1_ = 1) THEN
minute_ := ' minute';
ELSE
minute_ := ' minutes';
END IF;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := m1_ || minute_ || format_ || s1_ || second_;
RETURN return_;
ELSIF (precision_ = 1) THEN
s1_ := (((((d1_ * 24) + h1_) * 60) + m1_) * 60) + s1_;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := s1_ || second_;
RETURN return_;
ELSE
ms_ := ((((((d1_ * 24) + h1_) * 60) + m1_) * 60) + s1_) * 1000;
IF (ms_ = 1) THEN
msecond_ := ' millisecond';
ELSE
msecond_ := ' milliseconds';
END IF;
return_ := ms_ || msecond_;
RETURN return_;
END IF;
END Core;
BEGIN
RETURN(Core(start_, end_, syntax_));
END Return_Elapsed_Time;
For example, if I called this function right now (12.10.2018 11:17:00.00) using Return_Elapsed_Time(TO_TIMESTAMP('12.04.2017 12:00:00.00', 'DD.MM.YYYY HH24:MI:SS.FF'),SYSTIMESTAMP), it should return something like:
47344620000 milliseconds
Better to use procedure like that:
CREATE OR REPLACE FUNCTION timestamp_diff
(
start_time_in TIMESTAMP
, end_time_in TIMESTAMP
)
RETURN NUMBER
AS
l_days NUMBER;
l_hours NUMBER;
l_minutes NUMBER;
l_seconds NUMBER;
l_milliseconds NUMBER;
BEGIN
SELECT extract(DAY FROM end_time_in-start_time_in)
, extract(HOUR FROM end_time_in-start_time_in)
, extract(MINUTE FROM end_time_in-start_time_in)
, extract(SECOND FROM end_time_in-start_time_in)
INTO l_days, l_hours, l_minutes, l_seconds
FROM dual;
l_milliseconds := l_seconds*1000 + l_minutes*60*1000 + l_hours*60*60*1000 + l_days*24*60*60*1000;
RETURN l_milliseconds;
END;
You can check it by calling:
SELECT timestamp_diff (TO_TIMESTAMP('12.04.2017 12:00:00.00', 'DD.MM.YYYY HH24:MI:SS.FF'),
TO_TIMESTAMP('12.04.2017 12:00:01.111', 'DD.MM.YYYY HH24:MI:SS.FF'))
as milliseconds
FROM DUAL;
The timestamp casted correctly between formats else there is a chance the fields would be misinterpreted.
Here is a working sample that is correct when two different dates (Date2, Date1) are considered from table TableXYZ.
SELECT ROUND (totalSeconds / (24 * 60 * 60), 1) TotalTimeSpendIn_DAYS,
ROUND (totalSeconds / (60 * 60), 0) TotalTimeSpendIn_HOURS,
ROUND (totalSeconds / 60) TotalTimeSpendIn_MINUTES,
ROUND (totalSeconds) TotalTimeSpendIn_SECONDS
FROM (SELECT ROUND (
EXTRACT (DAY FROM timeDiff) * 24 * 60 * 60
+ EXTRACT (HOUR FROM timeDiff) * 60 * 60
+ EXTRACT (MINUTE FROM timeDiff) * 60
+ EXTRACT (SECOND FROM timeDiff))
totalSeconds,
FROM (SELECT TO_TIMESTAMP (
TO_CHAR (Date2,
'yyyy-mm-dd HH24:mi:ss')
- 'yyyy-mm-dd HH24:mi:ss'),
TO_TIMESTAMP (
TO_CHAR (Date1,
'yyyy-mm-dd HH24:mi:ss'),
'yyyy-mm-dd HH24:mi:ss')
timeDiff
FROM TableXYZ))
Above one has some syntax error, Please use following on oracle:
SELECT ROUND (totalSeconds / (24 * 60 * 60), 1) TotalTimeSpendIn_DAYS,
ROUND (totalSeconds / (60 * 60), 0) TotalTimeSpendIn_HOURS,
ROUND (totalSeconds / 60) TotalTimeSpendIn_MINUTES,
ROUND (totalSeconds) TotalTimeSpendIn_SECONDS
FROM
(SELECT ROUND ( EXTRACT (DAY FROM timeDiff) * 24 * 60 * 60 + EXTRACT (HOUR FROM timeDiff) * 60 * 60 + EXTRACT (MINUTE FROM timeDiff) * 60 + EXTRACT (SECOND FROM timeDiff)) totalSeconds
FROM
(SELECT TO_TIMESTAMP(TO_CHAR( date2 , 'yyyy-mm-dd HH24:mi:ss'), 'yyyy-mm-dd HH24:mi:ss') - TO_TIMESTAMP(TO_CHAR(date1, 'yyyy-mm-dd HH24:mi:ss'),'yyyy-mm-dd HH24:mi:ss') timeDiff
FROM TABLENAME
)
);
I've posted here some methods to convert interval to nanoseconds and nanoseconds to interval. These methods have a nanosecond precision.
You just need to adjust it to get milliseconds instead of nanoseconds.
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
))) * 100 AS MILLIS FROM DUAL;
MILLIS
1598434427263.027
I) if you need to calculate the elapsed time in seconds between two timestamp columns try this:
SELECT
extract ( day from (end_timestamp - start_timestamp) )*86400
+ extract ( hour from (end_timestamp - start_timestamp) )*3600
+ extract ( minute from (end_timestamp - start_timestamp) )*60
+ extract ( second from (end_timestamp - start_timestamp) )
FROM table_name
II) if you want to just show the time difference in character format try this:
SELECT to_char (end_timestamp - start_timestamp) FROM table_name
I know that many people finding this solution simple and clear:
create table diff_timestamp (
f1 timestamp
, f2 timestamp);
insert into diff_timestamp values(systimestamp-1, systimestamp+2);
commit;
select cast(f2 as date) - cast(f1 as date) from diff_timestamp;
bingo!
Related
I'm trying to find a way to get the holiday hours while comparing two dates.
the query below at the moment excludes the holidays and returns the working hours. I'm struggling to get it the other way. Just to return number of hours of holidays which falls between start and end date.
If the start date is of 15-04-22 16:00 and its a holiday then query should only return hours b/w 16:00 - 18:00. (work hours would be between 7:00 - 18:00)
create table holidays_tb(
holiday_date date
);
insert into holidays_tb values (TO_DATE('15/04/2022', 'DD/MM/YYYY'));
insert into holidays_tb values (TO_DATE('18/04/2022', 'DD/MM/YYYY'));
declare
v_st_date date;
v_end_date date;
return_val number;
begin
v_st_date := TO_DATE('13/04/2022 09:55:52', 'DD/MM/YYYY HH24:MI:SS');
v_end_date := TO_DATE('19/04/2022 16:30:00', 'DD/MM/YYYY HH24:MI:SS');
with all_days as
(select trunc(v_st_date) + level - 1 as a_dt
from dual
connect by level <= 1 + (v_end_date - v_st_date)
minus
select holiday_date from holidays_tb
)
select sum (11)
into return_val
from all_days
where TO_CHAR ( a_dt , 'Dy') NOT IN ('Sat', 'Sun');
dbms_output.put_line( return_val );
end;
Have been stuck at it for more than couple of hours now :|
You can get the number of holiday hours using:
DECLARE
v_st_date date := DATE '2022-04-13' + INTERVAL '0 09:55:52' DAY TO SECOND;
v_end_date date := DATE '2022-04-19' + INTERVAL '0 16:30:00' DAY TO SECOND;
v_work_day_start INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 07:00:00' DAY TO SECOND;
v_work_day_end INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 18:00:00' DAY TO SECOND;
v_hours NUMBER := EXTRACT(HOUR FROM v_work_day_end - v_work_day_start)
+ EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/60
+ EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/3600;
v_holiday NUMBER;
return_val number;
BEGIN
SELECT SUM(
LEAST(holiday_date + v_work_day_end, v_end_date)
- GREATEST(holiday_date + v_work_day_start, v_st_date)
) * 24
INTO v_holiday
FROM holidays_tb
WHERE holiday_date BETWEEN TRUNC(v_st_date) AND v_end_date
AND holiday_date - TRUNC(holiday_date, 'IW') < 5;
DBMS_OUTPUT.PUT_LINE( v_holiday );
END;
/
Which, for the sample data, outputs 22.
Additionally, you do not need to use a recursive query to get the number of days and can directly calculate it using:
DECLARE
v_st_date date := DATE '2022-04-13' + INTERVAL '0 09:55:52' DAY TO SECOND;
v_end_date date := DATE '2022-04-19' + INTERVAL '0 16:30:00' DAY TO SECOND;
v_work_day_start INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 07:00:00' DAY TO SECOND;
v_work_day_end INTERVAL DAY(0) TO SECOND(0) := INTERVAL '0 18:00:00' DAY TO SECOND;
v_hours NUMBER := EXTRACT(HOUR FROM v_work_day_end - v_work_day_start)
+ EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/60
+ EXTRACT(MINUTE FROM v_work_day_end - v_work_day_start)/3600;
v_holiday NUMBER;
return_val number;
BEGIN
SELECT SUM(
LEAST(holiday_date + v_work_day_end, v_end_date)
- GREATEST(holiday_date + v_work_day_start, v_st_date)
) * 24
INTO v_holiday
FROM holidays_tb
WHERE holiday_date BETWEEN TRUNC(v_st_date) AND v_end_date
AND holiday_date - TRUNC(holiday_date, 'IW') < 5;
return_val :=
-- Full weeks
(TRUNC(v_end_date, 'IW') - TRUNC(v_st_date, 'IW')) * 5 / 7 * v_hours
-- Full days before in start week
- LEAST(TRUNC(v_st_date) - TRUNC(v_st_date, 'IW'), 5) * v_hours
-- Part days before in start week
- CASE
WHEN v_st_date - TRUNC(v_st_date, 'IW') < 5
THEN LEAST(
GREATEST(
(v_st_date - (TRUNC(v_st_date) + v_work_day_start))* 24,
0
),
v_hours
)
ELSE 0
END
-- End full days
+ LEAST(TRUNC(v_end_date) - TRUNC(v_end_date, 'IW'), 5) * v_hours
-- End part days
+ CASE
WHEN v_end_date - TRUNC(v_end_date, 'IW') < 5
THEN LEAST(
GREATEST(
(v_end_date - (TRUNC(v_end_date) + v_work_day_start))* 24,
0
),
v_hours
)
ELSE 0
END
-- Holiday hours
- v_holiday;
DBMS_OUTPUT.PUT_LINE( return_val );
END;
/
Which outputs: 28.5689 (33 hours for 3 full days less 1.5 hours at the end and almost 3 hours at the start).
I'm trying to make a function on psql. It will be triggered on insert on table. I want to inject an variable on my select. Can't get working ...
CREATE OR REPLACE FUNCTION updateHistoricLongTime()
RETURNS void AS $$
DECLARE
hour_nb int;
index_hour int;
saved_hours int;
tmp_counter int;
BEGIN
hour_nb := 0;
index_hour := 1;
saved_hours := 2160;
tmp_counter := 0;
SELECT COUNT(*) FROM locationhistoric WHERE type='hour' AND idLocation=6 INTO hour_nb;
IF (hour_nb<saved_hours) THEN
FOR i IN 1 .. saved_hours LOOP
SELECT COUNT(*) FROM visits
WHERE stend < (timestamp '2017-11-29 15:00' - interval **>> index_hour<<<** - 1 ' hour') AND stend > (timestamp '017-11-29 15:00' - interval **>>index_hour <<<**' hour') AND location_id=6 AND duration>0 INTO tmp_counter;
index_hour := index_hour + 1;
END LOOP;
END IF;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE;
How can I inject variable index_hour in my SELECT COUNT(*) FROM Visits ...
EDIT: It's just syntax issue, but I can't manage to find the right way !
The result in command line:
ERROR: syntax error at or near "index_hour"
LINE 16: ... stend < (timestamp '2017-11-29 15:00' - interval index_hour...
Thanks a lot,
The solution
CREATE OR REPLACE FUNCTION updateHistoricLongTime()
RETURNS void AS $$
DECLARE
hour_nb int;
index_hour int;
saved_hours int;
tmp_counter int;
index_hour_minor int;
BEGIN
hour_nb := 0;
index_hour := 1;
index_hour_minor := 0;
saved_hours := 2160;
SELECT COUNT(*)
INTO hour_nb
FROM locationhistoric
WHERE type='hour'
AND idLocation=6;
IF (hour_nb<saved_hours) THEN
FOR i IN 1 .. saved_hours LOOP
SELECT COUNT(*)
INTO tmp_counter
FROM visits
WHERE start > timestamp '2017-11-29 15:00' - ( interval '1 hour' * index_hour )
AND start < timestamp '2017-11-29 15:00' - ( interval '1 hour' * index_hour_minor)
AND location_id=6
AND duration>0;
INSERT INTO locationhistoric
(type, date, counter, idLocation)
VALUES( 'hour',
timestamp '2017-11-29 15:00' - ( interval '1 hour' * index_hour_minor),
tmp_counter,
6);
index_hour_minor := index_hour_minor + 1;
index_hour := index_hour + 1;
END LOOP;
END IF;
END;
$$
LANGUAGE plpgsql;
The value specified for an interval can't be passed as a variable. However if the base unit is always an hour you can multiply a one our interval with the desired number of ours, e.g.
interval '1' hour * 5
will return 5 hours. The 5 can be a parameter. So your query should be:
SELECT COUNT(*)
INTO tmp_counter
FROM visits
WHERE stend < (timestamp '2017-11-29 15:00' - (interval '1' hour * index_hour))
AND stend > (timestamp '2017-11-29 15:00' - (interval '1' hour * index_hour))
AND location_id=6
AND duration > 0;
The syntax you want to get for your query (where index_hour = 8, for example) is:
select count(*)
from visits
where
stend < (timestamp '2017-11-29 15:00' - interval '7 hour') and
stend > (timestamp '2017-11-29 15:00' - interval '8 hour') and
location_id = 6 and
duration > 0;
Note where the quotes are. This means that your variable has to be inside quotes in pl/pgsql and that means it will be treated as a literal.
The solution is:
execute
'select count(*) ' ||
'from visits ' ||
'where ' ||
'stend < (timestamp ''2017-11-29 15:00'' - interval ''' || (index_hour - 1) || ' hour'') and ' ||
'stend > (timestamp ''2017-11-29 15:00'' - interval ''' || index_hour || ' hour'') and ' ||
'location_id = 6 and ' ||
'duration > 0'
To save me setting up your data I've written a simpler example using a table that I have (driver) so that I could test. Note that you have to use 2 single quotes to get one single quote into a string and that means counting quotes carefully.
create function a47768241() returns integer
as $body$
declare
index_hour int;
id integer;
begin
index_hour = 8;
execute
'select id ' ||
'from driver ' ||
'where ' ||
'from_date_time < (timestamp ''2013-04-22 16:00:00'' - interval ''' || (index_hour - 1) || ' hour'') '
into id;
return id;
end;
$body$
language 'plpgsql';
Simple test:
# select a47768241();
a47768241
-----------
158
(1 row)
Using the result value to check the date:
# select * from driver where id = a47768241();
id | vehicle_id | person_id | from_date_time | to_date_time | created_at | updated_at
-----+------------+-----------+---------------------------+---------------------------+----------------------------+------------
158 | 6784 | 15430 | 2012-09-13 17:00:41.39778 | 2012-09-14 01:54:46.39778 | 2016-06-03 16:43:11.456063 |
(1 row)
just concat the interval value, like
interval concat(index_hour - 1 , ' hour')
I have a requirement to display user available time in Hours:Minutes:Seconds format from a given total number of seconds value. Appreciate if you know a ORACLE function to do the same. I'm using Oracle.
Thank you for your time.
If you're just looking to convert a given number of seconds into HH:MI:SS format, this should do it
SELECT
TO_CHAR(TRUNC(x/3600),'FM9900') || ':' ||
TO_CHAR(TRUNC(MOD(x,3600)/60),'FM00') || ':' ||
TO_CHAR(MOD(x,60),'FM00')
FROM DUAL
where x is the number of seconds.
Try this one.
Very simple and easy to use
select to_char(to_date(10000,'sssss'),'hh24:mi:ss') from dual;
The following code is less complex and gives the same result. Note that 'X' is the number of seconds to be converted to hours.
In Oracle use:
SELECT TO_CHAR (TRUNC (SYSDATE) + NUMTODSINTERVAL (X, 'second'),
'hh24:mi:ss'
) hr
FROM DUAL;
In SqlServer use:
SELECT CONVERT(varchar, DATEADD(s, X, 0), 108);
If you have a variable containing f.e. 1 minute(in seconds), you can add it to the systimestamp then use to_char to select the different time parts from it.
select to_char(systimestamp+60/(24*60*60), 'yyyy.mm.dd HH24:mi:ss') from dual
For the comment on the answer by vogash, I understand that you want something like a time counter, thats because you can have more than 24 hours. For this you can do the following:
select to_char(trunc(xxx/3600)) || to_char(to_date(mod(xxx, 86400),'sssss'),':mi:ss') as time
from dual;
xxx are your number of seconds.
The first part accumulate the hours and the second part calculates the remaining minutes and seconds. For example, having 150023 seconds it will give you 41:40:23.
But if you always want have hh24:mi:ss even if you have more than 86000 seconds (1 day) you can do:
select to_char(to_date(mod(xxx, 86400),'sssss'),'hh24:mi:ss') as time
from dual;
xxx are your number of seconds.
For example, having 86402 seconds it will reset the time to 00:00:02.
Unfortunately not... However, there's a simple trick if it's going to be less than 24 hours.
Oracle assumes that a number added to a date is in days. Convert the number of seconds into days. Add the current day, then use the to_date function to take only the parts your interested in. Assuming you have x seconds:
select to_char(sysdate + (x / ( 60 * 60 * 24 ) ), 'HH24:MI:SS')
from dual
This won't work if there's more than 24 hours, though you can remove the current data again and get the difference in days, hours, minutes and seconds.
If you want something like: 51:10:05, i.e. 51 hours, 10 minutes and 5 seconds then you're going to have to use trunc.
Once again assuming that you have x seconds...
The number of hours is trunc(x / 60 / 60)
The number of minutes is trunc((x - ( trunc(x / 60 / 60) * 60 * 60 )) / 60)
The number of seconds is therefore the x - hours * 60 * 60 - minutes * 60
Leaving you with:
with hrs as (
select x, trunc(x / 60 / 60) as h
from dual
)
, mins as (
select x, h, trunc((x - h * 60 * 60) / 60) as m
from hrs
)
select h, m, x - (h * 60 * 60) - (m * 60)
from mins
I've set up a SQL Fiddle to demonstrate.
The following is Yet Another Way (tm) - still involves a little calculation but provides an example of using EXTRACT to pull the individual fields out of an INTERVAL:
DECLARE
SUBTYPE BIG_INTERVAL IS INTERVAL DAY(9) TO SECOND;
i BIG_INTERVAL;
nSeconds NUMBER := 86400000;
FUNCTION INTERVAL_TO_HMS_STRING(inv IN BIG_INTERVAL)
RETURN VARCHAR2
IS
nHours NUMBER;
nMinutes NUMBER;
nSeconds NUMBER;
strHour_format VARCHAR2(10) := '09';
workInv INTERVAL DAY(9) TO SECOND(9);
BEGIN
nHours := EXTRACT(HOUR FROM inv) + (EXTRACT(DAY FROM inv) * 24);
strHour_format := TRIM(RPAD(' ', LENGTH(TRIM(TO_CHAR(ABS(nHours)))), '0') || '9');
nMinutes := ABS(EXTRACT(MINUTE FROM inv));
nSeconds := ABS(EXTRACT(SECOND FROM inv));
RETURN TRIM(TO_CHAR(nHours, strHour_format)) || ':' ||
TRIM(TO_CHAR(nMInutes, '09')) || ':' ||
TRIM(TO_CHAR(nSeconds, '09'));
END INTERVAL_TO_HMS_STRING;
BEGIN
i := NUMTODSINTERVAL(nSeconds, 'SECOND');
DBMS_OUTPUT.PUT_LINE('i (fields) = ' || INTERVAL_TO_HMS_STRING(i));
END;
The code which extracts the fields, etc, still has to contain a calculation to convert the DAY field to equivalent hours, and is not the prettiest, but wrapped up neatly in a procedure it's not too bad to use.
Share and enjoy.
Assuming your time is called st.etime below and stored in seconds, here is what I use. This handles times where the seconds are greater than 86399 seconds (which is 11:59:59 pm)
case when st.etime > 86399 then to_char(to_date(st.etime - 86400,'sssss'),'HH24:MI:SS') else to_char(to_date(st.etime,'sssss'),'HH24:MI:SS') end readable_time
My version. Show Oracle DB uptime in format DDd HHh MMm SSs
select to_char(trunc((((86400*x)/60)/60)/24)) || 'd ' ||
to_char(trunc(((86400*x)/60)/60)-24*(trunc((((86400*x)/60)/60)/24)), 'FM00') || 'h ' ||
to_char(trunc((86400*x)/60)-60*(trunc(((86400*x)/60)/60)), 'FM00') || 'm ' ||
to_char(trunc(86400*x)-60*(trunc((86400*x)/60)), 'FM00') || 's' "UPTIME"
from (select (sysdate - t.startup_time) x from V$INSTANCE t);
idea from Date / Time Arithmetic with Oracle 9/10
Convert minutes to hour:min:sec format
SELECT
TO_CHAR(TRUNC((MINUTES * 60) / 3600), 'FM9900') || ':' ||
TO_CHAR(TRUNC(MOD((MINUTES * 60), 3600) / 60), 'FM00') || ':' ||
TO_CHAR(MOD((MINUTES * 60), 60), 'FM00') AS MIN_TO_HOUR FROM DUAL
For greater than 24 hours you can include days with the following query. The returned format is days:hh24:mi:ss
Query:
select trunc(trunc(sysdate) + numtodsinterval(9999999, 'second')) - trunc(sysdate) || ':' || to_char(trunc(sysdate) + numtodsinterval(9999999, 'second'), 'hh24:mi:ss') from dual;
Output:
115:17:46:39
create or replace procedure mili(num in number)
as
yr number;
yrsms number;
mon number;
monsms number;
wk number;
wksms number;
dy number;
dysms number;
hr number;
hrsms number;
mn number;
mnsms number;
sec number;
begin
yr := FLOOR(num/31556952000);
yrsms := mod(num, 31556952000);
mon := FLOOR(yrsms/2629746000);
monsms := mod(num,2629746000);
wk := FLOOR(monsms/(604800000));
wksms := mod(num,604800000);
dy := floor(wksms/ (24*60*60*1000));
dysms :=mod(num,24*60*60*1000);
hr := floor((dysms)/(60*60*1000));
hrsms := mod(num,60*60*1000);
mn := floor((hrsms)/(60*1000));
mnsms := mod(num,60*1000);
sec := floor((mnsms)/(1000));
dbms_output.put_line(' Year:'||yr||' Month:'||mon||' Week:'||wk||' Day:'||dy||' Hour:'||hr||' Min:'||mn||' Sec: '||sec);
end;
/
begin
mili(12345678904234);
end;
create or replace function `seconds_hh_mi_ss` (seconds in number)
return varchar2
is
hours_var number;
minutes_var number;
seconds_var number;
remeinder_var number;
output_var varchar2(32);
begin
select seconds - mod(seconds,3600) into hours_var from dual;
select seconds - hours_var into remeinder_var from dual;
select (remeinder_var - mod(remeinder_var,60)) into minutes_var from dual;
select seconds - (hours_var+minutes_var) into seconds_var from dual;
output_var := hours_var/3600||':'||minutes_var/60||':'||seconds_var;
return(output_var);
end;
/
You should check out this site. The TO_TIMESTAMP section could be useful for you!
Syntax:
TO_TIMESTAMP ( string , [ format_mask ] [ 'nlsparam' ] )
How to convert number 1.33408564814814 to time 32:01:05?
If you actually want the result as a character string, you could use a function like this:
set serveroutput on format wrapped;
declare
function days_to_time (p_days number) return varchar2
is
d number := p_days;
h integer;
m integer;
s integer;
begin
h := trunc(d*24);
d := d - h/24;
m := trunc(d*24*60);
d := d - m/24/60;
s := round(d*24*60*60);
return(h||':'||to_char(m,'FM00')||':'||TO_CHAR(s,'FM00'));
end;
begin
dbms_output.put_line(days_to_time(1.33408564814814));
end;
/
anonymous block completed
32:01:05
SELECT
-- EXTRACT(day FROM numtodsinterval(1.33408564814814 , 'DAY')) Days,
EXTRACT(day FROM numtodsinterval(1.33408564814814 , 'DAY')) * 24 + EXTRACT(hour FROM numtodsinterval(1.33408564814814 , 'DAY')) TotalHours,
-- EXTRACT(hour FROM numtodsinterval(1.33408564814814 , 'DAY')) Hours,
EXTRACT(minute FROM numtodsinterval(1.33408564814814 , 'DAY')) Minutes,
EXTRACT(second FROM numtodsinterval(1.33408564814814 , 'DAY')) Seconds
FROM dual;
The original number is an interval expressed as a number of days. The following is a bit ugly but might help show what's going on:
SELECT days,
hours,
minutes,
seconds,
TO_CHAR(days, 'FM9') || ' ' ||
TO_CHAR(day_hours, 'FM09') || ':' ||
TO_CHAR(minutes, 'FM09') || ':' ||
TO_CHAR(seconds, 'FM09') AS interval_string,
TO_DSINTERVAL(TO_CHAR(days, 'FM9') || ' ' ||
TO_CHAR(day_hours, 'FM09') || ':' ||
TO_CHAR(minutes, 'FM09') || ':' ||
TO_CHAR(seconds, 'FM09')) actual_interval
FROM (SELECT float_val,
days, hours, minutes,
hours - (days * 24) AS day_hours,
ROUND((((decimal_hours - hours) * 60) - trunc((decimal_hours - hours) * 60)) * 60) AS seconds
FROM (SELECT float_val,
trunc(hours / 24) AS days,
decimal_hours,
hours,
(decimal_hours - hours) * 60 AS decimal_minutes,
trunc((decimal_hours - hours) * 60) AS minutes
FROM (SELECT float_val,
float_val * 24 AS decimal_hours,
TRUNC(float_val* 24) AS hours
FROM (SELECT 1.33408564814814 AS float_val FROM dual))));
Share and enjoy.
Why isn't my PL/SQL duration function working correctly? In the query below, I manually calculate the 'hh:mm' the same way as in the function below. However, I get different results.
Calling Query:
WITH durdays AS (SELECT SYSDATE - (SYSDATE - (95 / 1440)) AS durdays FROM DUAL)
SELECT TRUNC (24 * durdays) AS durhrs,
MOD (TRUNC (durdays * 1440), 60) AS durmin,
steven.duration (SYSDATE - (95 / 1400), SYSDATE) duration
FROM durdays
Output:
durhrs: 1
durmin: 35
duration: '1:38'
Function code:
CREATE OR REPLACE FUNCTION steven.duration (d1 IN DATE, d2 IN DATE)
RETURN VARCHAR2 IS
tmpvar VARCHAR2 (30);
durdays NUMBER (20,10); -- Days between two DATEs
durhrs BINARY_INTEGER; -- Completed hours
durmin BINARY_INTEGER; -- Completed minutes
BEGIN
durdays := d2-d1;
durhrs := TRUNC(24 * durdays);
durmin := MOD (TRUNC(durdays * 1440), 60);
tmpvar := durhrs || ':' || durmin;
RETURN tmpvar;
END duration;
/
I think you might have a small typo:
WITH durdays AS (SELECT SYSDATE - (SYSDATE - (95 / 1440)) AS durdays FROM DUAL)
^^^^
SELECT TRUNC (24 * durdays) AS durhrs,
MOD (TRUNC (durdays * 1440), 60) AS durmin,
lims_sys.duration (SYSDATE - (95 / 1400), SYSDATE) duration
^^^^
Once this is fixed it works OK:
SQL> WITH durdays AS (SELECT SYSDATE - (SYSDATE - (95 / 1440)) AS durdays FROM DUAL)
2 SELECT TRUNC (24 * durdays) AS durhrs,
3 MOD (TRUNC (durdays * 1440), 60) AS durmin,
4 duration (SYSDATE - (95 / 1440), SYSDATE) duration
5 FROM durdays
6 ;
DURHRS DURMIN DURATION
---------- ---------- ----------------
1 34 1:34
sql>set time on
Then you will get the prompt like:
20:24:35 sql> select count(*) from v$session;
count(*)
-----------
78
20:24:50 sql>
Now you can come to know the query duration is 15sec's