Here is a function that is fast and flexible. You can count any weekday in a date range.
CREATE OR REPLACE FUNCTION wfportal.cx_count_specific_weekdays( p_week_days VARCHAR2 DEFAULT 'MON,TUE,WED,THU,FRI'
, p_start_date DATE
, p_end_date DATE)
RETURN NUMBER
IS
/***************************************************************************************************************
*
* FUNCTION DESCRIPTION:
*
* This function calculates the total required week days in a date range.
*
* PARAMETERS:
*
* p_week_days VARCHAR2 The week days that need to be counted, comma seperated e.g. MON,TUE,WED,THU,FRU,SAT,SUN
* p_start_date DATE The start date
* p_end_date DATE The end date
*
* CHANGE history
*
* No. Date Changed by Change Description
* ---- ----------- ------------- -------------------------------------------------------------------------
* 0 07-May-2013 yourname Created
*
***************************************************************************************************************/
v_date_end_first_date_range DATE;
v_date_start_last_date_range DATE;
v_total_days_in_the_weeks NUMBER;
v_total_days_first_date_range NUMBER;
v_total_days_last_date_range NUMBER;
v_output NUMBER;
v_error_text CX_ERROR_CODES.ERROR_MESSAGE%TYPE;
--Count the required days in a specific date ranges by using a list of all the weekdays in that range.
CURSOR c_total_days ( v_start_date DATE
, v_end_date DATE ) IS
SELECT COUNT(*) total_days
FROM ( SELECT ( v_start_date + level - 1) days
FROM dual
CONNECT BY LEVEL <= ( v_end_date - v_start_date ) + 1
)
WHERE INSTR( ',' || p_week_days || ',', ',' || TO_CHAR( days, 'DY', 'NLS_DATE_LANGUAGE=english') || ',', 1 ) > 0
;
--Calculate the first and last date range by retrieving the first Sunday after the start date and the last Monday before the end date.
--Calculate the total amount of weeks in between and multiply that with the total required days.
CURSOR c_calculate_new_dates ( v_start_date DATE
, v_end_date DATE ) IS
SELECT date_end_first_date_range
, date_start_last_date_range
, (
(
( date_start_last_date_range - ( date_end_first_date_range + 1 ) )
) / 7
) * total_required_days total_days_in_the_weeks --The total amount of required days
FROM ( SELECT v_start_date + DECODE( TO_CHAR( v_start_date, 'DY', 'NLS_DATE_LANGUAGE=english')
, 'MON', 6
, 'TUE', 5
, 'WED', 4
, 'THU', 3
, 'FRI', 2
, 'SAT', 1
, 'SUN', 0
, 0 ) date_end_first_date_range
, v_end_date - DECODE( TO_CHAR( v_end_date, 'DY', 'NLS_DATE_LANGUAGE=english')
, 'MON', 0
, 'TUE', 1
, 'WED', 2
, 'THU', 3
, 'FRI', 4
, 'SAT', 5
, 'SUN', 6
, 0 ) date_start_last_date_range
, REGEXP_COUNT( p_week_days, ',' ) + 1 total_required_days --Count the commas + 1 to get the total required weekdays
FROM dual
)
;
BEGIN
--Verify that the start date is before the end date
IF p_start_date < p_end_date THEN
--Get the new calculated days.
OPEN c_calculate_new_dates( p_start_date, p_end_date );
FETCH c_calculate_new_dates INTO v_date_end_first_date_range
, v_date_start_last_date_range
, v_total_days_in_the_weeks;
CLOSE c_calculate_new_dates;
--Calculate the days in the first date range
OPEN c_total_days( p_start_date, v_date_end_first_date_range );
FETCH c_total_days INTO v_total_days_first_date_range;
CLOSE c_total_days;
--Calculate the days in the last date range
OPEN c_total_days( v_date_start_last_date_range, p_end_date );
FETCH c_total_days INTO v_total_days_last_date_range;
CLOSE c_total_days;
--Sum the total required days
v_output := v_total_days_first_date_range + v_total_days_last_date_range + v_total_days_in_the_weeks;
ELSE
v_output := 0;
END IF;
RETURN v_output;
EXCEPTION
WHEN OTHERS
THEN
RETURN NULL;
END cx_count_specific_weekdays;
/