How to convert timestamp with milliseconds to date in Oracle - sql

I have MSSTAMP as "timestamp with milliseconds" in Oracle, format: 1483228800000. How can I cast that milliseconds timestamp into a date format "YYYY-MM", in order to get count of FINISHED rows per month for previous years.
I have tried different variations of TO_DATE, CAST, TO_CHAR - but I'm unable to get this working.
select
count(*) "EVENTS",
TO_DATE(MSSTAMP, 'YYYY-MM') "FINISHED_MONTH"
from
DB_TABLE
where
MSSTAMP < '1483228800000'
and
STATUS in ('FINISHED')
group by
FINISHED_MONTH ASC

Unix Time
If you just need to convert from milliseconds since epoch to a timestamp in the UTC timezone, then:
SELECT TIMESTAMP '1970-01-01 00:00:00.000 UTC'
+ NUMTODSINTERVAL( 1483228800000 / 1000, 'SECOND' )
AS TIME
FROM DUAL
Which outputs:
TIME
2017-01-01 00:00:00.000 +00:00
It you just want the year-month then use TRUNC( timestamp, 'MM' ) or TO_CHAR( timestamp, 'YYYY-MM' ).
Real Time (with leap seconds)
If you need to handle leap seconds then you can create a utility package that will adjust the epoch time to account for this:
CREATE OR REPLACE PACKAGE time_utils
IS
FUNCTION milliseconds_since_epoch(
in_datetime IN TIMESTAMP,
in_epoch IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
) RETURN NUMBER;
FUNCTION milliseconds_epoch_to_ts (
in_milliseconds IN NUMBER,
in_epoch IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
) RETURN TIMESTAMP;
END;
/
CREATE OR REPLACE PACKAGE BODY time_utils
IS
-- List of the seconds immediately following leap seconds:
leap_seconds CONSTANT SYS.ODCIDATELIST := SYS.ODCIDATELIST(
DATE '1972-07-01',
DATE '1973-01-01',
DATE '1974-01-01',
DATE '1975-01-01',
DATE '1976-01-01',
DATE '1977-01-01',
DATE '1978-01-01',
DATE '1979-01-01',
DATE '1980-01-01',
DATE '1981-07-01',
DATE '1982-07-01',
DATE '1983-07-01',
DATE '1985-07-01',
DATE '1988-01-01',
DATE '1990-01-01',
DATE '1991-01-01',
DATE '1992-07-01',
DATE '1993-07-01',
DATE '1994-07-01',
DATE '1996-01-01',
DATE '1997-07-01',
DATE '1999-01-01',
DATE '2006-01-01',
DATE '2009-01-01',
DATE '2012-07-01',
DATE '2015-07-01',
DATE '2016-01-01'
);
HOURS_PER_DAY CONSTANT BINARY_INTEGER := 24;
MINUTES_PER_HOUR CONSTANT BINARY_INTEGER := 60;
SECONDS_PER_MINUTE CONSTANT BINARY_INTEGER := 60;
MILLISECONDS_PER_SECOND CONSTANT BINARY_INTEGER := 1000;
MINUTES_PER_DAY CONSTANT BINARY_INTEGER := HOURS_PER_DAY * MINUTES_PER_HOUR;
SECONDS_PER_DAY CONSTANT BINARY_INTEGER := MINUTES_PER_DAY * SECONDS_PER_MINUTE;
MILLISECONDS_PER_MINUTE CONSTANT BINARY_INTEGER := SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
MILLISECONDS_PER_HOUR CONSTANT BINARY_INTEGER := MINUTES_PER_HOUR * MILLISECONDS_PER_MINUTE;
MILLISECONDS_PER_DAY CONSTANT BINARY_INTEGER := HOURS_PER_DAY * MILLISECONDS_PER_HOUR;
FUNCTION milliseconds_since_epoch(
in_datetime IN TIMESTAMP,
in_epoch IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
) RETURN NUMBER
IS
p_leap_milliseconds BINARY_INTEGER := 0;
p_diff INTERVAL DAY(9) TO SECOND(3);
BEGIN
IF in_datetime IS NULL OR in_epoch IS NULL THEN
RETURN NULL;
END IF;
p_diff := in_datetime - in_epoch;
IF in_datetime >= in_epoch THEN
FOR i IN 1 .. leap_seconds.COUNT LOOP
EXIT WHEN in_datetime < leap_seconds(i);
IF in_epoch < leap_seconds(i) THEN
p_leap_milliseconds := p_leap_milliseconds + MILLISECONDS_PER_SECOND;
END IF;
END LOOP;
ELSE
FOR i IN REVERSE 1 .. leap_seconds.COUNT LOOP
EXIT WHEN in_datetime > leap_seconds(i);
IF in_epoch > leap_seconds(i) THEN
p_leap_milliseconds := p_leap_milliseconds - MILLISECONDS_PER_SECOND;
END IF;
END LOOP;
END IF;
RETURN MILLISECONDS_PER_SECOND * EXTRACT( SECOND FROM p_diff )
+ MILLISECONDS_PER_MINUTE * EXTRACT( MINUTE FROM p_diff )
+ MILLISECONDS_PER_HOUR * EXTRACT( HOUR FROM p_diff )
+ MILLISECONDS_PER_DAY * EXTRACT( DAY FROM p_diff )
+ p_leap_milliseconds;
END milliseconds_since_epoch;
FUNCTION milliseconds_epoch_to_ts(
in_milliseconds IN NUMBER,
in_epoch IN TIMESTAMP DEFAULT TIMESTAMP '1970-01-01 00:00:00'
) RETURN TIMESTAMP
IS
p_datetime TIMESTAMP;
BEGIN
IF in_milliseconds IS NULL OR in_epoch IS NULL THEN
RETURN NULL;
END IF;
p_datetime := in_epoch
+ NUMTODSINTERVAL( in_milliseconds / MILLISECONDS_PER_SECOND, 'SECOND' );
IF p_datetime >= in_epoch THEN
FOR i IN 1 .. leap_seconds.COUNT LOOP
EXIT WHEN p_datetime < leap_seconds(i);
IF in_epoch < leap_seconds(i) THEN
p_datetime := p_datetime - INTERVAL '1' SECOND;
END IF;
END LOOP;
ELSE
FOR i IN REVERSE 1 .. leap_seconds.COUNT LOOP
EXIT WHEN p_datetime > leap_seconds(i);
IF in_epoch > leap_seconds(i) THEN
p_datetime := p_datetime + INTERVAL '1' SECOND;
END IF;
END LOOP;
END IF;
RETURN p_datetime;
END milliseconds_epoch_to_ts;
END;
/
Then you can do:
SELECT TIME_UTILS.milliseconds_epoch_to_ts(
in_milliseconds => 1483228800000,
in_epoch => TIMESTAMP '1970-01-01 00:00:00.000'
) AS time
FROM DUAL;
And get the output:
TIME
2016-12-31 23:59:33.000
Note: you will need to keep the package up-to-date when new leap-seconds are proposed.
db<>fiddle here
Update:
Your query would then be:
SELECT COUNT(*) "EVENTS",
TRUNC(
TIMESTAMP '1970-01-01 00:00:00.000'
+ NUMTODSINTERVAL( MSSTAMP / 1000, 'SECOND' ),
'MM'
) AS FINISHED_MONTH
FROM DB_TABLE
WHERE MSSTAMP < 1483228800000
AND STATUS = 'FINISHED'
GROUP BY
TRUNC(
TIMESTAMP '1970-01-01 00:00:00.000'
+ NUMTODSINTERVAL( MSSTAMP / 1000, 'SECOND' ),
'MM'
);

Related

MOD command calling functions

I have two functions, which are working fine. I want to use the MOD or decode command to call the functions with different options but I can't seem to get the code below to work.
Below is my test CASE. Any help would be greatly appreciated. Thanks in advance for your time and expertise.
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-YYYY HH24:MI:SS.FF';
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS'
CREATE OR REPLACE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP,
p_fraction IN VARCHAR2 DEFAULT 'Y'
) RETURN TIMESTAMP
IS
return_val_y TIMESTAMP := p_from + dbms_random.value () * (p_to - p_from + INTERVAL '1' DAY);
return_val_n TIMESTAMP (0) := return_val_y;
BEGIN
RETURN CASE
WHEN UPPER (SUBSTR (p_fraction, 1, 1)) = 'Y'
THEN return_val_y
ELSE return_val_N
END;
END random_timestamp;
/
CREATE OR REPLACE FUNCTION random_interval(
p_min IN NUMBER,
p_max IN NUMBER,
p_duration IN VARCHAR2,
p_fraction IN VARCHAR2 DEFAULT 'Y'
) RETURN INTERVAL DAY TO SECOND
IS
return_val_y INTERVAL DAY TO SECOND := NUMTODSINTERVAL(DBMS_RANDOM.VALUE(p_min, p_max), p_duration);
return_val_n INTERVAL DAY TO SECOND :=
( EXTRACT(DAY FROM return_val_y) * 24 * 60 * 60
+ EXTRACT(HOUR FROM return_val_y) * 60 * 60
+ EXTRACT(MINUTE FROM return_val_y) * 60
+ FLOOR(EXTRACT(SECOND FROM return_val_y))
) * INTERVAL '1' SECOND;
BEGIN
RETURN CASE
WHEN UPPER (SUBSTR (p_fraction, 1, 1)) = 'Y'
THEN return_val_y
ELSE return_val_N
END;
END random_interval;
/
/* can't get this to work */
SELECT
CASE MOD(LEVEL, 2)
WHEN 0
THEN
random_timestamp(TIMESTAMP '2022-04-01 00:00:00', TIMESTAMP '2022-04-30 00:00:00', 'Y') as ts,
random_interval(1, 10, 'HOUR', 'Y') as invr
ELSE
random_timestamp(TIMESTAMP '2022-04-01 00:00:00', TIMESTAMP '2022-04-30 00:00:00', 'N') as ts,
random_interval(1, 10, 'HOUR', 'N') as invr
END
FROM dual
CONNECT BY level <= 10;
A CASE expression returns a single expression; not multiple expressions. If you want multiple expressions then move the CASE from wrapping both of them (which is wrong) to inside each of the function calls:
SELECT random_timestamp(
TIMESTAMP '2022-04-01 00:00:00',
TIMESTAMP '2022-04-30 00:00:00',
CASE MOD(LEVEL, 2) WHEN 0 THEN 'Y' ELSE 'N' END
) as ts,
random_interval(
1,
10,
'HOUR',
CASE MOD(LEVEL, 2) WHEN 0 THEN 'Y' ELSE 'N' END
) as invr
FROM dual
CONNECT BY level <= 10;
fiddle

Oracle SQL Time Difference in hours

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).

Add days/hours/minutes and months to time stamp

Is there a way to add days hours months and minutes into a timestamp that i want to insert the value of into a variable
I have separate variables for minutes,hours,days and months that I want to add to the timestamp
the timestamp format goes like this '04-FEB-21 10.25.12.013000 AM'
I tried using SELECT TO_TIMESTAMP(datecreated,'dd-mon-yyyy hh.mi.ss AM') + daysvar into duedate FROM dual; but it returns the error AM/A.M. or PM/P.M. required
daysvar contains the amount of days to be added to the timestamp
Thank you!
I have separate variables for minutes,hours,days and months that I want to add to the timestamp
Use NUMTODSINTERVAL:
DECLARE
datecreated VARCHAR2(30) := '04-FEB-21 10.25.12.013000 AM';
days INT := 1;
hours INT := 2;
minutes INT := 42;
seconds INT := 3;
duedate VARCHAR2(30);
BEGIN
duedate := TO_CHAR(
TO_TIMESTAMP(
datecreated,
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
)
+ NUMTODSINTERVAL(days, 'DAY')
+ NUMTODSINTERVAL(hours, 'HOUR')
+ NUMTODSINTERVAL(minutes, 'MINUTE')
+ NUMTODSINTERVAL(seconds, 'SECOND'),
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
);
DBMS_OUTPUT.PUT_LINE(duedate);
END;
/
db<>fiddle here
If you want to add months then use ADD_MONTHS; however, it returns a DATE (and not a TIMESTAMP) so you would lose the fractional seconds. You can add those back though:
DECLARE
datecreated VARCHAR2(30) := '04-FEB-21 10.25.12.013000 AM';
months INT := 4;
days INT := 1;
hours INT := 2;
minutes INT := 42;
seconds INT := 3;
created_date TIMESTAMP;
duedate VARCHAR2(30);
BEGIN
created_date := TO_TIMESTAMP(
datecreated,
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
);
duedate := TO_CHAR(
CAST(ADD_MONTHS(created_date, months) AS TIMESTAMP)
+ NUMTODSINTERVAL(days, 'DAY')
+ NUMTODSINTERVAL(hours, 'HOUR')
+ NUMTODSINTERVAL(minutes, 'MINUTE')
+ NUMTODSINTERVAL(seconds, 'SECOND')
+ (created_date - CAST(created_date AS DATE)), -- Fractional seconds
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
);
DBMS_OUTPUT.PUT_LINE(duedate);
END;
/
db<>fiddle here

Calculating the working days between two dates in oracle

Below is my function where i tried to calculate the number of working days between two dates.
CREATE OR REPLACE function workingdays
( start_date IN DATE , end_date IN DATE)
return number
as
total_days varchar2(10);
v_count integer:= 0;
end_value date := to_date(end_date , 'YYYY-MM-DD');
start_value date := to_date(start_date , 'YYYY-MM-DD');
date_diff number(10);
begin
while(start_value <= end_value)
loop
if (to_char(TO_DATE(start_value,'YYYY-MM-DD') , 'D') = 1 or to_char(TO_DATE(start_value,'YYYY-MM-DD'), 'D') = 7)
then v_count := v_count+1;
end if;
start_value := start_value + 1;
end loop;
date_diff :=to_number( to_date( end_date, 'YYYY-MM-DD') - to_date (start_date , 'YYYY-MM-DD')) ;
total_days := to_char( (to_number(date_diff) - to_number(v_count)) + 1);
return (' The total working days is' || to_number((total_days)));
end;
/
The function compiles successfully, but on executing it, I get an error message at the return line. Could someone guide me. It is some issue the number/character conversion.
You make a lot of useless conversions, almost all of them can be skipped.
Result of TO_CHAR(..., 'D') depends on current user NLS_TERRITORY value, so you should not use it unless you ensure a certain NLS_TERRITORY setting.
This one is working:
CREATE OR REPLACE FUNCTION workingdays(start_date IN DATE , end_date IN DATE) RETURN VARCHAR2 AS
total_days NUMBER;
v_count INTEGER:= 0;
end_value DATE := TRUNC(end_date);
start_value DATE := TRUNC(start_date);
date_diff NUMBER;
BEGIN
WHILE start_value <= end_value LOOP
IF TO_CHAR(start_value, 'fmDay', 'NLS_DATE_LANGUAGE = american') IN ('Saturday','Sunday') THEN
v_count := v_count + 1;
END IF;
start_value := start_value + 1;
END LOOP;
date_diff := end_value - TRUNC(start_date);
total_days := date_diff - v_count + 1;
RETURN ' The total working days is ' || total_days;
END;
/
There are many other possibilities, this is just another way of doing it:
CREATE OR REPLACE FUNCTION workingdays(start_date IN DATE, end_date IN DATE) RETURN VARCHAR2 AS
next_run_date DATE := TRUNC(start_date);
total_days INTEGER := 0;
BEGIN
LOOP
DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=DAILY;INTERVAL=1;BYDAY=MON,TUE,WED,THU,FRI', NULL, next_run_date, next_run_date);
EXIT WHEN next_run_date >= end_date;
total_days := total_days + 1;
END LOOP;
RETURN ' The total working days is ' || total_days;
END;
select min(a_day)
as from_date
,max(a_day)
as till_date
,sum(decode(to_char(a_day,'D')
,1,0
,7,0
,1
)
)
as wrk_days_count
from (select trunc(sysdate,'iw') + level - 1
as a_day
from dual
connect by level < 8)
;

Calculating difference between two timestamps in Oracle in milliseconds

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!