Вот один из способов сделать это с union all
и оконными функциями:
select *
from (
select '06:00:00'::time start_time, min(start_time) end_time from mytable
union all
select end_time, lead(start_time) over(order by start_time) from mytable
union all
select max(end_time), '23:59:59'::time from mytable
) t
where start_time <> end_time
Немного сложно подробно объяснить, как это работает, но: первый объединенный запрос вычисляет интервал между 6:00 и начало первой смены, второй подзапрос обрабатывает объявленную смену, а последняя обрабатывает интервал между последней сменой и полуночью. Затем внешний запрос фильтрует записи, которые имеют пробелы. Чтобы понять, как это работает, вы можете независимо запустить подзапрос и посмотреть, как правильно начинается и заканчивается.
Демонстрация на DB Fiddle :
start_time | end_time
:--------- | :-------
06:00:00 | 09:00:00
15:00:00 | 17:00:00
22:00:00 | 23:59:59