I did not check for possible duplicates, but one way to do it is recursive query:
with cte(id, start_date, end_date, hd1, hd2) as (
select id, cast(start_date as date), cast(end_date as date), cast(start_date as date),
cast(least(end_date, trunc(start_date) + 17/24) as date)
from jobs
union all
select id, start_date, end_date, cast(trunc(hd1) + 1 + 8/24 as date),
cast(least(trunc(hd1) + 1 + 17/24, end_date) as date)
from cte
where trunc(hd1) + 1 + 8/24 < end_date)
select id, start_date, end_date, dbms_xplan.format_time_s(sum(hd2 - hd1) * 24 * 60) hours
from cte
where to_char(hd1, 'd') not in (6, 7)
group by id, start_date, end_date
dbfiddle demo
Where I use 17/24
it means ending hour 17:00
, 8/24
- starting hour, not in (6, 7)
excludes saturdays and sundays.
Edit 1: It should be 24 * 60 * 60 in last select.
Edit 2: To make query independent of nls_settings use:
to_char(hd1, 'Dy', 'nls_date_language=english') not in ('Sat', 'Sun')
dbfiddle demo
the version of Oracle I'm using insists on having SELECT as the first
word in any code
Such recursive queries are available from Oracle version 11. I don't know if your tool requires select
in the first line or is this version problem, but in first case you can move RCTE to from
clause:
select id, start_date, end_date, dbms_xplan.format_time_s(sum(hd2 - hd1) * 24 * 60 * 60) hours
from (
with
cte(id, start_date, end_date, hd1, hd2) as (
select id, cast(start_date as date), cast(end_date as date), cast(start_date as date),
cast(least(end_date, trunc(start_date) + 17/24) as date)
from jobs
union all
select id, start_date, end_date, cast(trunc(hd1) + 1 + 8/24 as date),
cast(least(trunc(hd1) + 1 + 17/24, end_date) as date)
from cte
where trunc(hd1) + 1 + 8/24 < end_date)
select * from cte
where to_char(hd1, 'Dy', 'nls_date_language=english') not in ('Sat', 'Sun') )
group by id, start_date, end_date
fiddle