Oto rozwiązanie tylko dla SQL. Użyłem DATETIME dla kolumn. Przechowywanie czasu osobno jest moim zdaniem błędem, ponieważ będziesz miał problemy, gdy minie północ. Możesz to dostosować, aby poradzić sobie z tą sytuacją, jeśli zajdzie taka potrzeba. W rozwiązaniu założono również, że godziny rozpoczęcia i zakończenia NIE są NULL. Ponownie, możesz dostosować w razie potrzeby, jeśli tak nie jest.
Ogólnym sednem rozwiązania jest uzyskanie wszystkich czasów początkowych, które nie pokrywają się z żadnymi innymi rozpiętościami, pobranie wszystkich czasów zakończenia, które nie nakładają się na żadne rozpiętości, a następnie dopasowanie ich do siebie.
Wyniki są zgodne z oczekiwanymi wynikami, z wyjątkiem jednego przypadku, w którym ręczne sprawdzenie wygląda tak, jakbyś miał błąd w oczekiwanym wyniku. Szóstego powinno być rozpiętość, która kończy się 2009-06-06 10:18:45.000.
SELECT
ST.start_time,
ET.end_time
FROM
(
SELECT
T1.start_time
FROM
dbo.Test_Time_Spans T1
LEFT OUTER JOIN dbo.Test_Time_Spans T2 ON
T2.start_time < T1.start_time AND
T2.end_time >= T1.start_time
WHERE
T2.start_time IS NULL
) AS ST
INNER JOIN
(
SELECT
T3.end_time
FROM
dbo.Test_Time_Spans T3
LEFT OUTER JOIN dbo.Test_Time_Spans T4 ON
T4.end_time > T3.end_time AND
T4.start_time <= T3.end_time
WHERE
T4.start_time IS NULL
) AS ET ON
ET.end_time > ST.start_time
LEFT OUTER JOIN
(
SELECT
T5.end_time
FROM
dbo.Test_Time_Spans T5
LEFT OUTER JOIN dbo.Test_Time_Spans T6 ON
T6.end_time > T5.end_time AND
T6.start_time <= T5.end_time
WHERE
T6.start_time IS NULL
) AS ET2 ON
ET2.end_time > ST.start_time AND
ET2.end_time < ET.end_time
WHERE
ET2.end_time IS NULL