Since you have 12c, you should learn the MATCH_RECOGNIZE() clause, which is quite often the most efficient solution when time periods are involved.
Here I sort by end date, get the first row, then find all the rows whose start date is less than or equal to any preceding end date. That bunch of rows is a "match". Once I get that, I start again with the next row after the "match".
drop table t purge;
create table t (STAFF_NUMBER, SHIFT_DATE, TASK_START_TIME, TASK_END_TIME) as select
123, to_date('12/10/2016','mm/dd/yyyy'), to_date('12/10/2016 17:14','mm/dd/yyyy hh24/mi'), to_date('12/10/2016 20:10','mm/dd/yyyy hh24/mi') from dual union all select
123, to_date('12/10/2016','mm/dd/yyyy'), to_date('12/10/2016 20:08','mm/dd/yyyy hh24/mi'), to_date('12/10/2016 21:08','mm/dd/yyyy hh24/mi') from dual union all select
123, to_date('12/10/2016','mm/dd/yyyy'), to_date('12/10/2016 22:50','mm/dd/yyyy hh24/mi'), to_date('12/10/2016 23:28','mm/dd/yyyy hh24/mi') from dual union all select
123, to_date('12/10/2016','mm/dd/yyyy'), to_date('12/10/2016 21:53','mm/dd/yyyy hh24/mi'), to_date('12/11/2016 02:17','mm/dd/yyyy hh24/mi') from dual union all select
123, to_date('12/10/2016','mm/dd/yyyy'), to_date('12/11/2016 01:40','mm/dd/yyyy hh24/mi'), to_date('12/11/2016 04:21','mm/dd/yyyy hh24/mi') from dual;
select * from t
match_recognize(
partition by STAFF_NUMBER, SHIFT_DATE
order by TASK_END_TIME, TASK_START_TIME
measures min(TASK_START_TIME) TASK_START_TIME,
max(TASK_END_TIME) TASK_END_TIME
pattern(a b*)
define b as TASK_START_TIME <= last(TASK_END_TIME,1)
);
STAFF_NUMBER SHIFT_DATE TASK_START_TIME TASK_END_TIME
------------ ----------- ---------------- ----------------
123 2016-12-10 2016-12-10 17:14 2016-12-10 21:08
123 2016-12-10 2016-12-10 21:53 2016-12-11 04:21
I disagree with your expected output. The start time of the second output row should be 21:53 because it is earlier than 22:50.