Calculating the working days between two dates in oracle - sql

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

Related

Oracle SQL: Return a distinct list of days when inputting a min and max of a date field

Is the following the correct way to get a distinct list of days for a date range (min and max of a date field) I intend to create a sql view out of this:
with range as (
select min(date) start_date,
max(date) end_date
from table
)
select start_date + level - 1 AS "DATE",
extract(month from start_date + level - 1) AS "MONTH",
extract(year from start_date + level - 1) AS "YEAR"
from range
connect by level <= (
trunc(end_date) - trunc(start_date) + 1
);
Do you really need to create a DATE table when you can generate one on the fly if needed
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
/
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1'DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
/
SELECT
c.COLUMN_VALUE
FROM
TABLE(generate_dates_pipelined(DATE '2022-07-01',
DATE '2022-07-31')) c

Function Friday13 sql

I am reviewing the functions but I cannot solve this one;
Write a function that returns every Friday 13th during a specific year.
Example:
SELECT * FROM martes13(2020);
13/01/2020
13/03/2020
13/08/2020
My unfortunate attempt, do not pay much attention.
DECLARE
diaInicial date;
diaFinal date;
anio1 date;
anio2 date;
auxData date;
dates date[];
BEGIN
diaInicial := ('01/' || '01/' || anio ) :: date;
diaFinal := diaInicial + '1 YEAR' :: interval;
anio2:= date_part('year',diaFinal);
FOR i IN 1..12 BY 1 LOOP
FOR j IN 1..30 BY 1 LOOP
diaInicial := anio || '-' || i || '-' || j;
if(date_part('dom',auxData)==13 and date_part('dow',auxData)==5)then
dates[j] := diaInicial;
end if;
end loop;
end loop;
return dates;
END;
There's no way to solve it no matter how hard I try, I understand that I have to use dates, years intervals and counters but it does not work out. Any help or information could be of use to me.
Thanks in advance.
That can be solved with a simple SQL statement:
SELECT CAST(d AS date)
FROM generate_series(
TIMESTAMP '2020-01-13',
TIMESTAMP '2020-12-13',
INTERVAL '1 month'
) AS thirteen(d)
WHERE EXTRACT (dow FROM d) = 5;
You could wrap that in an SQL function.

PL/SQL loop over table type and extend

I'm currently learning PL/SQL and my task is to create a package which finds all German holidays and inserts them into one collection. My problem is that I have to insert them by looping over them and by extending the table. It's easy to extend the table manually but way too much work and my teacher won't allow the manual extends. I appreciate any solution or help.
CREATE OR REPLACE PACKAGE BODY pa_feiertage_mau IS
FUNCTION calc_holidays (in_year_c IN INTEGER)
RETURN DATE
IS
v_k INTEGER;
v_m INTEGER;
v_s INTEGER;
v_a INTEGER;
v_d INTEGER;
v_r INTEGER;
v_og INTEGER;
v_sz INTEGER;
v_oe INTEGER;
v_os INTEGER;
v_day INTEGER;
v_month INTEGER;
BEGIN
v_k := floor(in_year_c / 100);
v_m := 15 + floor((3 * v_k + 3) / 4) - floor((8 * v_k + 13) / 25);
v_s := 2 - floor((3 * v_k + 3) / 4);
v_a := MOD(in_year_c, 19);
v_d := MOD((19 * v_a + v_m), 30);
v_r := floor(v_d / 29) + (floor(v_d / 28) - floor(v_d / 29)) * floor(v_a / 11);
v_og := 21 + v_d - v_r;
v_sz := 7 - MOD((in_year_c + floor(in_year_c / 4) + v_s), 7);
v_oe := 7 - MOD(v_og - v_sz, 7);
v_os := v_og + v_oe;
IF (v_os <= 31) THEN
v_day := v_os;
v_month := 3;
ELSE
v_day := v_os - 31;
v_month := 4;
END IF;
RETURN TO_DATE(v_day || '.' || v_month || '.' || in_year_c, 'DD,MM,YYYY');
END calc_holidays;
FUNCTION get_holidays (in_year1 IN VARCHAR2)
RETURN Type_tab_feiertage
PIPELINED
IS
/*Static holidays */
v_year1 VARCHAR2(4) := in_year1;
v_neujahr VARCHAR2(10) := '01.01.';
v_3kings VARCHAR2(10) := '06.01.';
v_fraut VARCHAR2(10) := '08.03.';
v_arbeit VARCHAR2(10) := '01.05.';
v_himmel VARCHAR2(10) := '15.08.';
v_deu VARCHAR2(10) := '03.10.';
v_refo VARCHAR2(10) := '31.10.';
v_aller VARCHAR2(10) := '01.11.';
v_wh1 VARCHAR2(10) := '25.12.';
v_wh2 VARCHAR2(10) := '26.12.';
/*Changing holidays*/
d_ostern DATE := calc_holidays(TO_NUMBER(in_year1));
d_karf DATE := d_ostern - INTERVAL '2' DAY;
d_gruen DATE := d_ostern - INTERVAL '3' DAY;
d_osterm DATE := d_ostern + INTERVAL '1' DAY;
d_chimmel DATE := d_ostern + INTERVAL '39' DAY;
d_pfingsts DATE := d_ostern + INTERVAL '49' DAY;
d_pfingstm DATE := d_pfingsts + INTERVAL '1' DAY;
d_fronlei DATE := d_ostern + INTERVAL '60' DAY;
tab_feiertage type_tab_feiertage;
BEGIN
tab_feiertage := NEW type_tab_feiertage();
LOOP
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM := TO_DATE( v_neujahr || v_year1, 'DD.MM.YYYY');
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Neujahr';
END LOOP;
IF tab_feiertage.COUNT > 0 THEN
FOR i IN tab_feiertage.FIRST .. tab_feiertage.LAST
LOOP
PIPE ROW( tab_feiertage( i));
END LOOP;
--RETURN tab_feiertage;
END IF;
RETURN;
END get_holidays;
END pa_feiertage_mau;
To be honest it's hard to tell what your teacher is driving at. You have some fixed reference data keys (holiday names) and logic for deriving their reference values, and you need to put the keys and derived values in a collection. There is no magic way of doing this: each entry needs to be assigned manually.
Personally I would discard the local variables (v_%, d_%) and put that logic and the names in the collection populating code. The only variable I would keep is d_ostern, because you need that to anchor all the other moveable feasts. Note the change to how Pfingstm is derived.
d_ostern := calc_holidays(TO_NUMBER(in_year1));
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM := TO_DATE( '01.01.' || v_year1, 'DD.MM.YYYY');
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Neujahr';
...
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM := d_ostern;
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Ostern';
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM := d_ostern - INTERVAL '2' DAY;
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Karf';
...
tab_feiertage.EXTEND();
tab_feiertage( tab_feiertage.LAST).DATUM := d_ostern + INTERVAL '50' DAY;
tab_feiertage( tab_feiertage.LAST).FEIERTAG := 'Pfingstm');
...
There is a way of populating the collection without using manual extension and explicit assignment, but it requires SQL. Here we calculate the value of Ostern using a WITH clause to call the calc_holidays() function, then write a massive UNION ALL query which selects the key-value pairs and stores them in the collection using BULK COLLECT:
select datum, feiertag
bulk collect into tab_feiertage
from (
/* calculate moveable feast */
with hol as (
select calc_holidays(TO_NUMBER(in_year1)) as ostern
from dual )
/* Static holidays */
select to_date('01.01.'||in_year1, 'DD.MM.YYYY') as datum, 'Neujahr' as feiertag from hol union all
select to_date('06.01.'||in_year1, 'DD.MM.YYYY') as datum, '3kings' as feiertag from hol union all
select to_date('08.03.'||in_year1, 'DD.MM.YYYY') as datum, 'Fraut' as feiertag from hol union all
select to_date('01.05.'||in_year1, 'DD.MM.YYYY') as datum, 'Arbeit' as feiertag from hol union all
select to_date('15.08.'||in_year1, 'DD.MM.YYYY') as datum, 'Himmel' as feiertag from hol union all
select to_date('03.10.'||in_year1, 'DD.MM.YYYY') as datum, 'Deu' as feiertag from hol union all
select to_date('31.10.'||in_year1, 'DD.MM.YYYY') as datum, 'Refo' as feiertag from hol union all
select to_date('01.11.'||in_year1, 'DD.MM.YYYY') as datum, 'Aller' as feiertag from hol union all
select to_date('25.12.'||in_year1, 'DD.MM.YYYY') as datum, 'Wh1' as feiertag from hol union all
select to_date('26.12.'||in_year1, 'DD.MM.YYYY') as datum, 'Wh2' as feiertag from hol union all
/* Changing holidays */
select ostern , 'Ostern' as feiertag from hol union all
select ostern - INTERVAL '2' DAY as datum , 'Karf' as feiertag from hol union all
select ostern - INTERVAL '3' DAY as datum , 'Gruen' as feiertag from hol union all
select ostern + INTERVAL '1' DAY as datum , 'Osterm' as feiertag from hol union all
select ostern + INTERVAL '39' DAY as datum , 'Chimmel' as feiertag from hol union all
select ostern + INTERVAL '49' DAY as datum , 'Pfingsts' as feiertag from hol union all
select ostern + INTERVAL '50' DAY as datum , 'Pfingstm' as feiertag from hol union all
select ostern + INTERVAL '60' DAY as datum , 'Fronlei' as feiertag from hol
);
This may not be what your teacher expects but it works. There is a demo on db<>fiddle here

add n working days including custom holiday list in oracle plsql

I need to find the 'n'th business day in oracle plsql function which should exclude weekends and custom list of holidays. I got something like this however unable to fit the custom holiday logic in here,
create or replace function add_n_working_days (
start_date date, working_days pls_integer
) return date as
end_date date := start_date;
counter pls_integer := 0;
begin
if working_days = 0 then
end_date := start_date;
elsif to_char(start_date, 'fmdy') in ('sat', 'sun') then
end_date := next_day(start_date, 'monday');
end if;
while (counter < working_days) loop
end_date := end_date + 1;
if to_char(end_date, 'fmdy') not in ('sat', 'sun') then
counter := counter + 1;
end if;
end loop;
return end_date;
end add_n_working_days;
/
I have the custom holiday list in a different table which can be extracted using the sql
select holidays from holiday_table
I tried adding elsif condition with a sub query but that is not supported
if to_char(end_date, 'fmdy') not in ('sat', 'sun') then
counter := counter + 1;
elsif to_char(end_date, 'YYYYMMDD') in (select holidays from holiday_table) then
counter := counter + 1;
end if;
I will try with counting the number of weekend days with a similar loop:
WHILE (v_copy > 0) LOOP
end_date := end_date + 1;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := end_date + 1;
ELSE
v_copy := v_copy - 1;
END IF;
END LOOP;
And then after the looping process you can count the number of holidays in the table which are after the start_date and end_date and not in ('sat', 'sun').
SELECT COUNT(*) INTO v_custom_cnt
FROM holiday_table
WHERE to_char(holidays, 'fmdy') not in ('sat', 'sun') AND
holidays BETWEEN start_date AND end_date;
Now add this number to your end_date and you will get the date of the nth business day. Of course before returning check if the new end_date is in ('sat', 'sun').
end_date := end_date + v_custom_cnt;
IF to_char(holidays, 'fmdy') in ('sat', 'sun') THEN
end_date := next_day(end_date, 'monday');
END IF;
Finally the end_date is the date of the nth working day.
CREATE OR REPLACE FUNCTION add_n_working_days (
start_date DATE,
working_days PLS_INTEGER
) RETURN DATE AS
end_date DATE := start_date;
counter PLS_INTEGER := 0;
v_copy PLS_INTEGER := working_days;
v_custom_cnt INTEGER := 0;
BEGIN
IF working_days = 0 THEN
end_date := start_date;
ELSIF to_char(start_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := next_day(start_date, 'monday');
END IF;
WHILE (v_copy > 0) LOOP
end_date := end_date + 1;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := end_date + 1;
ELSE
v_copy := v_copy - 1;
END IF;
END LOOP;
SELECT COUNT(*) INTO v_custom_cnt
FROM holiday_table
WHERE to_char(end_date, 'fmdy') NOT IN ('sat', 'sun')
AND holidays BETWEEN start_date AND end_date;
end_date := end_date + v_custom_cnt;
IF to_char(end_date, 'fmdy') IN ('sat', 'sun') THEN
end_date := next_day(start_date, 'monday');
END IF;
RETURN end_date;
END add_n_working_days;
It is not 100% tested
Different approach: create a table that only lists business day and take nth value:
CREATE OR REPLACE FUNCTION add_n_working_days (
start_date DATE, working_days PLS_INTEGER
) RETURN DATE AS
l_end_date DATE := start_date;
l_counter pls_integer := 0;
BEGIN
SELECT
business_day
INTO l_end_date
FROM
(
WITH
dates AS
(SELECT start_date + level - 1 as dt FROM dual CONNECT BY level < 100)
,weekdates AS
(SELECT dt as weekday FROM dates WHERE TO_CHAR(dt,'fmdy') NOT IN ('sat','sun'))
,business_days AS
(
SELECT weekday as business_day FROM weekdates
MINUS
SELECT holiday FROM so_holidays
)
SELECT business_day, ROW_NUMBER() OVER (ORDER BY 1) as rn from business_days
)
WHERE rn = working_days + 1;
RETURN l_end_date;
END add_n_working_days;
SQL is set orientated so start thinking in terms of sets NOT iteration (no loops). Assuming you can designate a maximum number of days to be tested you can get what you want with a single simple query.
create or replace function add_n_working_days (
start_date date, working_days pls_integer
) return date as
working_date_ot date;
begin
with date_list as (select trunc(start_date) + level - 1 tdate
from dual
connect by level <= 5*working_days
)
select tdate
into working_date_ot
from ( select tdate, row_number() over(order by tdate) date_num
from date_list
where to_char(tdate,'fmdy') not in ('sat','sun')
and not exists
(select null
from holiday_dates
where trunc(holiday_date) = tdate
)
)
where date_num = working_days;
return working_date_ot;
end add_n_working_days;
How it works:
The function assumes the desired date exists within 5 times the number of days requested. So if 10 business hence are requested the resulting date will be within 50 days (that would be a whole lot of holidays if not).
The date_list CTE builds a list of potential dates out to the identified limit.
The sub select in the main filters the generated list eliminating Sat and Sun. It then probes the holiday table for the remaining dates and eliminating any any date in the table.
Remaining dates are numbered based on ascending value.
The main outer select then chooses the date number matching the working_days specified.
No Looping required. Just my .02 cents worth.

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;
/