PLSQL - Round Date to the nearest century? - sql

Here is the output I am trying to get:
Number of slashes: 2
In 25 days it is: 19-FEB-15
The raw number is:
1.666666667E-01
The rounded number is: .17
Nearest Century: 01-JAN-01
I'm really not sure how to round "dDate25" to the nearest century?
Here is the code I have so far:
SET SERVEROUTPUT ON
DECLARE
vFilePath VARCHAR2 (100) := 'c\Temp\ProcDB.mdf';
vFilePath1 VARCHAR2 (100);
nNumber16 FLOAT;
dDate25 DATE := SYSDATE+25;
BEGIN
vFilePath1 := REPLACE (vFILEPATH, '\');
DBMS_OUTPUT.PUT_LINE('Number of slashes:' || (LENGTH(vFilePath)-LENGTH(vFilePath1)));
DBMS_OUTPUT.PUT_LINE('In 25 days it is:' || to_char(dDate25, 'MM-DD-YYYY'));
nNumber16 := 1/6;
DBMS_OUTPUT.PUT_LINE('The raw number is: ' || to_char(nNumber16, '9.999999999EEEE'));
DBMS_OUTPUT.PUT_LINE('The rounded number is: ' || round(nNumber16, 2));
END;
Thanks,

You can use the TRUNC(date) function if you want to always 'round down' to the current century (so a date in 2051 gives 2001); or the ROUND(date) function if you want to round properly (so 2051 gives 2101). Either way, with the CC format mask to get the first day of the century:
SQL> select round(sysdate + 25, 'CC') from dual;
TRUNC(SYSDATE+25,'C
-------------------
2001-01-01 00:00:00
So you can use:
ROUND(dDate25)
And you can then format that however you need; without any NLS assumptions:
DBMS_OUTPUT.PUT_LINE('Nearest Century: '
|| TO_CHAR(ROUND(dDate25, 'CC'), 'DD-MON-RR', 'NLS_DATE_LANGUAGE=ENGLISH'));

DECLARE
dDate25 DATE;
numYear NUMBER;
BEGIN
dDate25 := TO_DATE ('2016-01-27', 'YYYY-MM-DD');
SELECT (ROUND (TO_CHAR (dDate25, 'YYYY') / 100, 0) * 100) + 1
INTO numYear
FROM DUAL;
DBMS_OUTPUT.PUT_LINE('First year of the nearest century to ' || TO_CHAR(dDate25, 'YYYY-MM-DD') || ' is: ' || numYear);
dDate25 := TO_DATE ('2116-01-27', 'YYYY-MM-DD');
SELECT (ROUND (TO_CHAR (dDate25, 'YYYY') / 100, 0) * 100) + 1
INTO numYear
FROM DUAL;
DBMS_OUTPUT.PUT_LINE('First year of the nearest century to ' || TO_CHAR(dDate25, 'YYYY-MM-DD') || ' is: ' || numYear);
dDate25 := TO_DATE ('2051-01-27', 'YYYY-MM-DD');
SELECT (ROUND (TO_CHAR (dDate25, 'YYYY') / 100, 0) * 100) + 1
INTO numYear
FROM DUAL;
DBMS_OUTPUT.PUT_LINE('First year of the nearest century to ' || TO_CHAR(dDate25, 'YYYY-MM-DD') || ' is: ' || numYear);
END;
Output:
First year of the nearest century to 2016-01-27 is: 2001
First year of the nearest century to 2116-01-27 is: 2101
First year of the nearest century to 2051-01-27 is: 2101
ROUND (TO_CHAR (dDate25, 'YYYY') / 100, 0) * 100 gives you the nearest century i.e. it would round 2055 up to 2100. Adding +1 gives you the first year of the nearest century.
You can then TO_DATE(numYear || '-01-01', 'YYYY-MM-DD') to get January 1 of the first year of the nearest century if you need it in a date variable.

You mean decade instead of century, right? Otherwise your two end digits will always be "01" :-)
Corrected version (my previous one always returned 01, because it was rounding to the next century):
SELECT SUBSTR(FLOOR(to_char(systimestamp, 'YYYY')/10)*10,3,1) || 1 FROM dual
translated to your code:
decadeStart varchar2(10) := '01-JAN-' || SUBSTR(FLOOR(to_char(dDate25, 'YYYY')/10)*10,3,1) || 1;
DBMS_OUTPUT.PUT_LINE('The decade starts with: ' || decadeStart);

Related

How do I get the day portion(as a string/char) from a date and run tests on it in PL/SQL?

Can someone just tell me why this doesn't work. As far as I know, I've put the string into the variable and wanted to test it but it didn't seem to work and brought up errors.
SET SERVEROUTPUT ON;
DECLARE
DATETODAY DATE := SYSDATE;
DAYT VARCHAR2(10);
BEGIN
SELECT TO_CHAR(SYSDATE,'DAY') INTO DAYT FROM DUAL;
DBMS_OUTPUT.PUT_LINE('The date today is '||DATETODAY ||' and it is ' ||DAYT);
IF DAYT = 'SATURDAY' OR DAYT = 'SUNDAY' THEN
: DBMS_OUTPUT.PUT_LINE('Today is '||DAYT||' and its is a weekend');
ELSE
: DBMS_OUTPUT.PUT_LINE('Today is '||DAYT||' and its a week day');
END IF;
END;
/
SELECT TO_CHAR (SYSDATE, 'day') DayName,
TO_CHAR (SYSDATE, 'd') DayOfWeek,
TO_CHAR (SYSDATE, 'dd') DayOfMonth,
TO_CHAR (SYSDATE, 'ddd') DayOfYear
FROM DUAL;
DayName is bound to the language of your db. Better use DayOfWeek.
Anyway you should run the example-sql provided to compare your output. Perhaps a UPPER() could also help you.
TO_DATE( date_value, 'DAY' ) returns a fixed-length string (not a variable-length); this means that it is right-padded with space characters:
SQL Fiddle
Query 1:
SELECT TO_CHAR( DATE '2018-05-05', 'DAY' ) AS day,
DUMP( TO_CHAR( DATE '2018-05-05', 'DAY' ) ) As dump
FROM DUAL
Results:
| DAY | DUMP |
|-----------|-----------------------------------------|
| SATURDAY | Typ=1 Len=9: 83,65,84,85,82,68,65,89,32 |
Shows that the final character has ASCII code 32 - a space.
So your code should be:
SET SERVEROUTPUT ON;
DECLARE
DATETODAY DATE := SYSDATE;
DAYT VARCHAR2(10);
BEGIN
DAYT := TO_CHAR( DATETODAY ,'DAY');
DBMS_OUTPUT.PUT_LINE('The date today is '||DATETODAY ||' and it is ' ||DAYT);
IF DAYT = 'SATURDAY ' OR DAYT = 'SUNDAY ' THEN
DBMS_OUTPUT.PUT_LINE('Today is '||DAYT||' and its is a weekend');
ELSE
DBMS_OUTPUT.PUT_LINE('Today is '||DAYT||' and its a week day');
END IF;
END;
/
Which (for my NLS_DATE_FORMAT setting of YYYY-MM-DD HH24:MI:SS) outputs:
The date today is 2018-05-04 14:32:25 and it is FRIDAY
Today is FRIDAY and its a week day
Changing the initial assignment to DATETODAY DATE := DATE '2018-05-05'; then the output is:
The date today is 2018-05-05 00:00:00 and it is SATURDAY
Today is SATURDAY and its is a weekend
However, you could also write it as:
DECLARE
DATETODAY DATE := SYSDATE;
DAYT VARCHAR2(10);
BEGIN
DAYT := TO_CHAR( DATETODAY ,'DAY');
DBMS_OUTPUT.PUT_LINE('The date today is '||DATETODAY ||' and it is ' ||DAYT);
IF DATETODAY - TRUNC( DATETODAY, 'IW' ) >= 5 THEN
DBMS_OUTPUT.PUT_LINE('Today is '||DAYT||' and its is a weekend');
ELSE
DBMS_OUTPUT.PUT_LINE('Today is '||DAYT||' and its a week day');
END IF;
END;
/
As TRUNC( DATETODAY, 'IW' ) will truncate the date back to the start of the ISO week (always midnight on Monday) and this is independent of any NLS_DATE_LANGUAGE or NLS_TERRITORY settings that affect the TO_CHAR function.

How to find epoch for last month same day?

I have below PLSQL code which finds the epoch for last month same day, however it fails when I run it on month end for 31 and 01 days.
SET serveroutput ON
DECLARE
vDay VARCHAR2(30) := '&Enter_current_day';
vDate VARCHAR2(30);
vEpoch NUMBER;
BEGIN
vDate := TO_CHAR(sysdate, 'MM')||'-'||vDay||'-'||TO_CHAR(sysdate, 'YYYY');
vEpoch := (ADD_MONTHS(TO_DATE(vDate, 'MM-DD-YYYY HH24:MI:SS'), -1) - TO_DATE('01/01/1970 00:00:00', 'MM-DD-YYYY HH24:MI:SS')) * 24 * 60 * 60;
vDate := TO_DATE(vDate, 'MM-DD-YYYY HH24:MI:SS');
dbms_output.put_line('Current Date: '||to_number(vDay)||' Epoch: '||vEpoch||' Date: '||vDate);
END;
e.g. if todays date is,
A. 30-Sep and if I enter '31' then it should return epoch for the 01-Sep
B. 30-Sep and if I enter '01' then it should return epoch for the 01-Sep
C. 30-Mar and if I enter '31' then it should return epoch for the 01-Mar
D. 30-Mar and if I enter '01' then it should return epoch for the 01-Mar
Maybe something like this would do the trick?
DECLARE
vday VARCHAR2(30) := '&Enter_current_day';
vdate DATE;
vepoch NUMBER;
v_today DATE := SYSDATE - 30;
BEGIN
vdate := to_date(to_char(v_today, 'MM') || '-' ||
least(to_number(vday),
to_number(to_char(last_day(v_today), 'dd'))) || '-' ||
to_char(v_today, 'YYYY'),
'MM-DD-YYYY');
vepoch := (add_months(vdate, -1) -
to_date('01/01/1970 00:00:00', 'MM-DD-YYYY HH24:MI:SS')) * 24 * 60 * 60;
dbms_output.put_line('Current Date: ' || to_number(vday) || ' Epoch: ' ||
vepoch || ' Date: ' || to_char(vdate, 'mm-dd-yyyy'));
END;
Current Date: 1 Epoch: 1470009600 Date: 09-01-2016
Current Date: 15 Epoch: 1471219200 Date: 09-15-2016
Current Date: 30 Epoch: 1472601600 Date: 09-30-2016
Current Date: 31 Epoch: 1472601600 Date: 09-30-2016
This simply finds the last day of the month returned by the v_today date and compares it to the input vday and picks the lowest value. So, for September, if vday = 31, it would compare 31 with 30 (the last day in September) and output 30.
If you're dead-set on returning the first day of the month, then you'd need to change the least section to something like:
CASE WHEN to_number(to_char(last_day(v_today), 'dd')) < to_number(vday)
THEN '01'
ELSE vday
END
N.B. Note that I've made a few changes to your code to make it more robust - eg. You had vDate := TO_DATE(vDate, 'MM-DD-YYYY HH24:MI:SS'); which is a bit daft since you originally had vDate as a string, and by doing that, you're forcing Oracle to implicitly convert the string into a date and then back into a string.
Instead, I declared vDate as a date and have output the date in resultant string in the date format you used throughout the rest of the code - this means that it no longer relies on the NLS nls_date_format parameter to decide (this parameter is not necessarily the same for everyone!).

PL/SQL (How to calculate the first and last day of any quarter of any year)

I have one table, per_all_peopl_f, with following columns:
name person_id emp_flag effective_start_date effective_end_date DOJ
--------------------------------------------------------------------------------
ABC 123 Y 30-MAR-2011 30-MAR-2013 10-FEB-2011
ABC 123 Y 24-FEB-2011 27-FEB-2011 10-FEB-2011
DEF 345 N 10-APR-2012 30-DEC-4712 15-SEP-2011
There are many entries (1000+) with repeated data and different effective start dates.
I have to calculate the Workforce headcount. That is, the number of employees that exits the company quarterly.
The following columns have to be fetched:
Headcount in 2012 (1st quarter)
Headcount in 2013 (1st quarter)
difference between the two headcounts
% difference
This has to be done quarterly. that is whichever quarter I pass the workforce headcount should be calculated according to that.
The query I have written is
CREATE OR REPLACE FUNCTION function_name
(l_end_date ,l_start_date )
RETURN number;
IS
l_emp
BEGIN
select count(distinct papf.person_id)
into l_emp
from per_all_people_f papf
where papf.emp_flag ='Y'
and effective_start_date >=l_end_date
and effective_end_date <=l_start_date ;
return l_emp;
END function_name;
create xx_pack_name body
is
crate or replace procedure proc_name( l_quarter number,
p_person_id number,
l_year number )
cursor cur_var
is
select function_name(l_start_date ,l_end_date ) EMP_2012,
function_name(l_start_date1,l_end_date1 ) EMP_2013,
function_name(l_start_date ,l_end_date )-function_name(l_start_date1,l_end_date1 ) Diff
from dual;
Begin
if(l_year=2012)
if l_quarter =1
then
l_start_date :='01-Jan-2013';
l_end_date :='31-APR-2013';
elsif l_quarter =2
then
l_start_date :='01-May-2013';
l_end_date :='31-Aug-2013';
else
l_start_date :='01-Sep-2013';
l_end_date :='31-Dec-2013';
end if;
end if;
if(l_year=2013)
then
if l_quarter =1
then
l_start_date :='01-Jan-2013';
l_end_date :='31-APR-2013';
elsif l_quarter =2
then
l_start_date :='01-May-2013';
l_end_date :='31-Aug-2013';
else
l_start_date :='01-Sep-2013';
l_end_date :='31-Dec-2013';
end if;
end if;
for cur_val in cur_var
loop
dbms_output.put_line(cur_var.emp_2012 || cur_var.emp_2013 ||cur_var.diff )
end loop
end xx_pack_name ;
This package is taking too long.
Is there any other way I can calculate the last and first day of quarter of any year ????
And also when I am calculating the
percentage function_name(l_start_date ,l_end_date
)-function_name(l_start_date1,l_end_date1 ) /100
the output is not coming in the select statement
I find this question very confusing. If the real question is how to calculate the quarter of an arbitrary DATE then there's already plenty of examples, like:
Oracle - break dates into quarters
Find First and Last Day of the last Quarter in ORACLE
How to calculate the quarter of an arbitrary date
Some dates for testing:
create table lots_of_dates as
select trunc(sysdate - level * 7) as d
from dual
connect by level <= 52;
Find the quarters:
select d,
to_char(d, 'YYYY-Q') as QUARTER,
trunc(d, 'Q') as Q_FIRST_DAY,
add_months(trunc(d, 'Q'), 3) - 1 as Q_LAST_DAY
from lots_of_dates
order by 1;
Results:
D QUARTE Q_FIRST_DAY Q_LAST_DAY
------------------ ------ ------------------ ------------------
02-SEP-12 2012-3 01-JUL-12 30-SEP-12
09-SEP-12 2012-3 01-JUL-12 30-SEP-12
16-SEP-12 2012-3 01-JUL-12 30-SEP-12
23-SEP-12 2012-3 01-JUL-12 30-SEP-12
30-SEP-12 2012-3 01-JUL-12 30-SEP-12
07-OCT-12 2012-4 01-OCT-12 31-DEC-12
14-OCT-12 2012-4 01-OCT-12 31-DEC-12
21-OCT-12 2012-4 01-OCT-12 31-DEC-12
28-OCT-12 2012-4 01-OCT-12 31-DEC-12
04-NOV-12 2012-4 01-OCT-12 31-DEC-12
11-NOV-12 2012-4 01-OCT-12 31-DEC-12
...
A PL/SQL procedure that returns the first and last days of a quarter
The quarter's start and end dates are constant for all years except the year part. I.e. the second quarter always begins on 1st April and end on 30th June on every year. Thus the day and month can be fixed and only year part have to be adjusted.
A function can only return one value so the subroutine is implemented as procedure instead. I also provided a function wrappers to the procedure:
-- raises CASE_NOT_FOUND for non-existing quarters
create or replace procedure get_quarter_days(
p_year in number,
p_quarter in number,
p_first_day out date,
p_last_day out date
) deterministic as
begin
case p_quarter
when 1 then
p_first_day := to_date(p_year || '-01-01', 'YYYY-MM-DD');
p_last_day := to_date(p_year || '-03-31', 'YYYY-MM-DD');
when 2 then
p_first_day := to_date(p_year || '-04-01', 'YYYY-MM-DD');
p_last_day := to_date(p_year || '-06-30', 'YYYY-MM-DD');
when 3 then
p_first_day := to_date(p_year || '-07-01', 'YYYY-MM-DD');
p_last_day := to_date(p_year || '-09-30', 'YYYY-MM-DD');
when 4 then
p_first_day := to_date(p_year || '-10-01', 'YYYY-MM-DD');
p_last_day := to_date(p_year || '-12-31', 'YYYY-MM-DD');
end case;
end;
/
show errors
create or replace function get_quarter_first_day(
p_year in number,
p_quarter in number
) return date deterministic as
v_first_day date;
v_last_day date;
begin
get_quarter_days(p_year, p_quarter, v_first_day, v_last_day);
return v_first_day;
end;
/
show errors
create or replace function get_quarter_last_day(
p_year in number,
p_quarter in number
) return date deterministic as
v_first_day date;
v_last_day date;
begin
get_quarter_days(p_year, p_quarter, v_first_day, v_last_day);
return v_last_day;
end;
/
show errors
How to use the subroutines above:
declare
v_first_day date;
v_last_day date;
begin
get_quarter_days(2011, 1, v_first_day, v_last_day);
dbms_output.put_line(v_first_day || ' - ' || v_last_day);
get_quarter_days(2012, 2, v_first_day, v_last_day);
dbms_output.put_line(v_first_day || ' - ' || v_last_day);
get_quarter_days(2013, 3, v_first_day, v_last_day);
dbms_output.put_line(v_first_day || ' - ' || v_last_day);
get_quarter_days(2014, 4, v_first_day, v_last_day);
dbms_output.put_line(v_first_day || ' - ' || v_last_day);
dbms_output.put_line(get_quarter_first_day(2015, 1) || ' - ' ||
get_quarter_last_day(2015, 1));
end;
/

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!

Oracle Convert Seconds to Hours:Minutes:Seconds

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' ] )