📌 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!