📌 TL;DR: In this post, we explore how to accurately detect time overlaps in Typescript, including cross-midnight cases. You’ll learn how to handle tricky edge cases and implement a robust solution using React, TypeScript, and functional programming techniques.

🔗 GitHub Repo: GitHub Link
🎬 Live Demo: Demo Link

🚀 The Problem: Detecting Time Overlaps

Time overlap detection is a common problem in scheduling applications. Suppose you have:

An availability window (e.g., 22:00 - 06:00)
A new event/test window (e.g., 23:30 - 02:00)

How do you check if they overlap, considering that both cross midnight?

Most naive implementations fail when dealing with time ranges that span across two days. In this post, we’ll build a robust solution to handle all possible cases!

🧐 Understanding the Edge Cases

Before jumping into code, let’s break down the possible scenarios:

🔥 Let’s jump into it 🏊🏼

A time window can be represented as an interval [start, end] where start and end are the minutes since midnight

(e.g., 22:00 → 1320 mins, 06:00 → 360 mins). If end < start, it crosses midnight (e.g., 22:00 - 06:00 → [1320, 360])

The key idea is:

✅ Two intervals overlap if and only if:

segment1.start < segment2.end && segment2.start < segment1.end

🏗️ Implementing solution

Let’s break the solution into logical steps:

1️⃣ Convert Time Strings (HH:mm) to Minutes

Converting a 24-hour circular timeline into a linear sequence of minutes greatly simplifies the logic.

const Time24FormatToMinutes = (time: string): number => {
  const [hours, minutes] = time.split(":").map(Number);
  return hours * 60 + minutes;
};

2️⃣ Define the Overlap Check Logic

const checkSegmentOverlap = (
  segment1: { start: number; end: number },
  segment2: { start: number; end: number }
): boolean => segment1.start < segment2.end && segment2.start < segment1.end;

3️⃣ Handle Midnight Cases

Creating a seamless timeline of minutes is achieved by adding 24 hours (1440 minutes) to time intervals that extend past midnight.

const handleMidnightCrossing = (start: number, end: number) => ({
  firstDay: { start, end: end === 0 ? 1440 : end },
  secondDay: end === 0 ? { start: 0, end } : null
});

4️⃣ Wrapping the logic in a React Hook

useEffect(() => {
    if (
      !availableStartTime ||
      !availableEndTime ||
      !testStartTime ||
      !testEndTime
    ) {
      setOverlaps(true);
      return;
    }

    const checkSegmentOverlap = (
      segment1: { start: number; end: number },
      segment2: { start: number; end: number }
    ) => {
      return segment1.start < segment2.end && segment2.start < segment1.end;
    };

    const availableStart = Time24FormatToMinutes(availableStartTime);
    const availableEnd = Time24FormatToMinutes(availableEndTime);
    const testStart = Time24FormatToMinutes(testStartTime);
    const testEnd = Time24FormatToMinutes(testEndTime);

    // Handle special case of midnight (0 minutes)
    // If end time is midnight (0), treat it as end of day (1440 minutes)
    const adjAvailEnd = availableEnd === 0 ? 1440 : availableEnd;
    const adjTestEnd = testEnd === 0 ? 1440 : testEnd;

    // Check if each range crosses midnight
    const availCrossesMidnight = availableStart >= adjAvailEnd;
    const testCrossesMidnight = testStart >= adjTestEnd;

    // For each range, create two "day segments" that represent
    // the parts of the range before and after midnight
    const availFirstDay = {
      start: availableStart,
      end: availCrossesMidnight ? 1440 : adjAvailEnd,
    };
    const availSecondDay = availCrossesMidnight
      ? { start: 0, end: adjAvailEnd }
      : null;

    const testFirstDay = {
      start: testStart,
      end: testCrossesMidnight ? 1440 : adjTestEnd,
    };
    const testSecondDay = testCrossesMidnight
      ? { start: 0, end: adjTestEnd }
      : null;

    // Check for overlap between all possible day segment combinations
    const firstDayOverlap = checkSegmentOverlap(availFirstDay, testFirstDay);

    const secondDayOverlap =
      availSecondDay &&
      testSecondDay &&
      checkSegmentOverlap(availSecondDay, testSecondDay);

    const crossDayOverlap1 =
      availSecondDay && checkSegmentOverlap(availSecondDay, testFirstDay);

    const crossDayOverlap2 =
      testSecondDay && checkSegmentOverlap(availFirstDay, testSecondDay);

    // If any segment combination overlaps, the ranges overlap
    const hasOverlap =
      firstDayOverlap ||
      secondDayOverlap ||
      crossDayOverlap1 ||
      crossDayOverlap2;

    setOverlaps(hasOverlap ?? false);
  }, [availableStartTime, availableEndTime, testStartTime, testEndTime]);

✅ Results
Using this approach, we can now correctly detect overlaps even when times cross midnight.

🔍 Final Thoughts
✅ This method accounts for all edge cases
✅ Handles time spans that cross midnight

If you’re building a scheduling system, booking app, or calendar tool, this technique will save you hours of debugging!

💬 What’s Next?
🛠️ Improve performance by memoizing calculations
📚 Use this logic in a backend API to validate time conflicts
Let me know your thoughts in the comments! 🚀

🎉 Happy Coding!