1

I have been working on a solution for detecting overlapping timespans in SQL Server. This is to prevent the overlap of events, or in my case, work shifts.

While there has been a lot of discussion on SO for finding overlapping date ranges, there is less on overlapping timespans, and even less when the timespans are allowed to cross midnight. Through lots of research and testing, I've come up with the following solution.

CREATE TABLE Shift
(
    ShiftID INT IDENTITY(1,1),
    StartTime TIME(0) NOT NULL,
    EndTime TIME(0) NOT NULL
);

CREATE PROCEDURE [dbo].[spInsertShift]
    @StartTime                      TIME(0),
    @EndTime                        TIME(0)
AS
BEGIN

    DECLARE @ThrowMessage           NVARCHAR(4000)

    -- Check whether the new shift would overlap with any existing shifts.
    IF EXISTS
    (
        SELECT 0
        FROM Shift
        WHERE 
        (
            -- Case #1: Neither shift crosses midnight.
            (@StartTime < @EndTime AND StartTime < EndTime)

            -- New shift would overlap with an existing shift.
            AND @StartTime < EndTime
            AND StartTime < @EndTime
        )
        OR
        (
            -- Case #2: Both shifts cross midnight.
            (@EndTime < @StartTime AND EndTime < StartTime)

            -- New shift would overlap with an existing shift.
            AND CAST(@StartTime AS DATETIME) < DATEADD(DAY, 1, CAST(EndTime AS DATETIME))
            AND CAST(StartTime AS DATETIME) < DATEADD(DAY, 1, CAST(@EndTime AS DATETIME))
        )
        OR
        (
            -- Case #3: New shift crosses midnight, but the existing shift does not.
            (@EndTime < @StartTime AND StartTime < EndTime)
            AND
            (
                -- New shift would overlap with an existing shift.
                @StartTime > StartTime AND @StartTime < EndTime
                OR @EndTime > StartTime AND @EndTime < EndTime
                OR
                (
                    -- Existing shift would be inside new shift.
                    CAST(StartTime AS DATETIME) BETWEEN CAST(@StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(@EndTime AS DATETIME))
                    AND CAST(EndTime AS DATETIME) BETWEEN CAST(@StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(@EndTime AS DATETIME))
                )
            )
        )
        OR
        (
            -- Case #4: New shift does not cross midnight, but the existing shift does.
            (@StartTime < @EndTime AND EndTime < StartTime)
            AND
            (
                -- Existing shift would overlap with new shift.
                StartTime > @StartTime AND StartTime < @EndTime
                OR EndTime > @StartTime AND EndTime < @EndTime
                OR
                (
                    -- New shift would be inside an existing shift.
                    CAST(@StartTime AS DATETIME) BETWEEN CAST(StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(EndTime AS DATETIME))
                    AND CAST(@EndTime AS DATETIME) BETWEEN CAST(StartTime AS DATETIME) AND DATEADD(DAY, 1, CAST(EndTime AS DATETIME))
                )
            )
        )
    )
    BEGIN
        SET @ThrowMessage = 'A Shift already exists in this timespan.'
        THROW 50140, @ThrowMessage, 1
    END

    INSERT INTO Shift
    (
        StartTime,
        EndTime
    )
    VALUES
    (
        @StartTime,
        @EndTime
    )

END

[SQL Fiddle]

Since my timespans are allowed to cross midnight, I had to use some mechanism to denote that the EndTime is greater than the StartTime, despite the fact that StartTime='22:00' > EndTime='06:00' for example. I chose to CAST the EndTime to a DATETIME and add a day, in these cases.

My question is: what is the best way to detect overlapping timespans that are allowed to cross midnight? My current solution feels overly verbose and complex. When testing, please keep all timespan test cases in mind. Thanks!

Jake Reece
  • 1,140
  • 1
  • 12
  • 23
  • 1
    Sample data and desired results would really help. – Gordon Linoff Mar 16 '18 at 14:26
  • 1
    What's wrong with your code? Is it throwing an error? Asking "what's the best way" is off-topic for SO, since it is a matter of opinion. Asking for suggestions on working code is also off-topic, because it belongs on codereview.stackexchange.com. – Tab Alleman Mar 16 '18 at 14:29
  • No, no errors. I was unsure whether to post the question here or on codereview, as I've seen so many other popular questions on SO that ask, "What is the best way...?", such as the question I linked about overlapping date ranges. – Jake Reece Mar 16 '18 at 14:49

2 Answers2

0

You should consider adding a date field in your table and try to process your data. In that way you can easily find-out the exact timespan of shifts.

  • The table is a configuration table for shift times and is meant to be date agnostic. You're right though, adding a date would make it much simpler. – Jake Reece Mar 16 '18 at 14:41
  • This isn't an answer. It is a comment. – dfundako Mar 16 '18 at 14:49
  • yes @dfundako , but i don't have enough reputation to comment :) – krunal patel Mar 16 '18 at 14:53
  • @krunalpatel - if you don't have enough rep yet, you should take that as a cue to *earn more rep*, by for instance answering questions that do not require clarification, rather than *abusing* the answer system (If it was intended that people with "not enough rep" should leave comments as answers, there *wouldn't be* a rep limit) – Damien_The_Unbeliever Mar 16 '18 at 15:45
0

It seems like use DATETIME instead of TIME data type solves your problem perfectly.

By having the value with day and time information, the spans across midnight are captured and can be compared with trivial effort.

Michael O'Neill
  • 946
  • 7
  • 22