# 确定两个date范围是否重叠

（StartA <= EndB）和（EndA> = StartB）

certificate：

`_ |---- DateRange A ------| |---Date Range B -----| _`
（如果`StartA > EndB`真）

`|---- DateRange A -----| _ _ |---Date Range B ----|`
（如果`EndA < StartB`真）

（如果一个范围既不完全相同，

`Not (A Or B)` <=> `Not A And Not B`

`>=`运算符更改为`>` ，将`<=`更改为`<`

{ `endA-startA``endA - startB``endB-startA``endB - startB` }

`(StartA <= EndB) and (EndA >= StartB)` `(StartA <= EndB) and (StartB <= EndA)`

`DateRangesOverlap = max(start1, start2) < min(end1, end2)`

`(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)`或者：
`(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)`
`(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))`或者：
`(Max(StartA, StartB) <= Min(EndA, EndB)`

`(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)`

` `(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)` `

` `// ------------------------------------------------------------------------ public enum PeriodRelation { After, StartTouching, StartInside, InsideStartTouching, EnclosingStartTouching, Enclosing, EnclosingEndTouching, ExactMatch, Inside, InsideEndTouching, EndInside, EndTouching, Before, } // enum PeriodRelation` `

` `A.end >= B.start AND A.start <= B.end` `

ErikE评论：

` `----------------------|-------A-------|---------------------- |----B1----| |----B2----| |----B3----| |----------B4----------| |----------------B5----------------| |----B6----| ----------------------|-------A-------|---------------------- |------B7-------| |----------B8-----------| |----B9----| |----B10-----| |--------B11--------| |----B12----| |----B13----| ----------------------|-------A-------|----------------------` `

` `overlap = max(0, min(EndDate1, EndDate2) - max(StartDate1, StartDate2)) if (overlap > 0) { ... }` `

` `|----------------------| range 1 |---> range 2 overlap |---> range 2 overlap |---> range 2 overlap |---> range 2 no overlap` `

` `def doesOverlap (r1, r2): if r1.s > r2.s: swap r1, r2 if r2.s > r1.e: return false return true` `

` `def doesOverlap (r1, r2): if r1.s > r2.s: swap r1, r2 return r2.s <= r1.e` `

` `|----------------------| range 1 |---> range 2 overlap |---> range 2 overlap |---> range 2 no overlap |---> range 2 no overlap` `

• 将空值处理为无穷大
• 假设下界是包含的，上界是排他的。
• 附带一堆testing

testing是基于整数，但由于JavaScript中的date对象是可比较的，所以您可以只input两个date对象。 或者你可以扔在毫秒时间戳。

# 码：

` `/** * Compares to comparable objects to find out whether they overlap. * It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive). * A null value is interpreted as infinity */ function intervalsOverlap(from1, to1, from2, to2) { return (to2 === null || from1 < to2) && (to1 === null || to1 > from2); }` `

# testing：

` `describe('', function() { function generateTest(firstRange, secondRange, expected) { it(JSON.stringify(firstRange) + ' and ' + JSON.stringify(secondRange), function() { expect(intervalsOverlap(firstRange[0], firstRange[1], secondRange[0], secondRange[1])).toBe(expected); }); } describe('no overlap (touching ends)', function() { generateTest([10,20], [20,30], false); generateTest([20,30], [10,20], false); generateTest([10,20], [20,null], false); generateTest([20,null], [10,20], false); generateTest([null,20], [20,30], false); generateTest([20,30], [null,20], false); }); describe('do overlap (one end overlaps)', function() { generateTest([10,20], [19,30], true); generateTest([19,30], [10,20], true); generateTest([10,20], [null,30], true); generateTest([10,20], [19,null], true); generateTest([null,30], [10,20], true); generateTest([19,null], [10,20], true); }); describe('do overlap (one range included in other range)', function() { generateTest([10,40], [20,30], true); generateTest([20,30], [10,40], true); generateTest([10,40], [null,null], true); generateTest([null,null], [10,40], true); }); describe('do overlap (both ranges equal)', function() { generateTest([10,20], [10,20], true); generateTest([null,20], [null,20], true); generateTest([10,null], [10,null], true); generateTest([null,null], [null,null], true); }); });` `

PhantomJS 1.9.8（Linux）：执行20的20成功（0.003秒/0.004秒）

` `StartDate1.IsBetween(StartDate2, EndDate2) || EndDate1.IsBetween(StartDate2, EndDate2)` `

`IsBetween`是类似的

` ` public static bool IsBetween(this DateTime value, DateTime left, DateTime right) { return (value > left && value < right) || (value < left && value > right); }` `

```  ---------------------- | ------- A ------- | ----------- -----------
| ---- B1 ---- |
| ---- ---- B2 |
| ---- ---- B3 |
| ---------- ---------- B4 |
| ---------------- B5 ---------------- |
| ---- ---- B6 |
---------------------- | ------- A ------- | ----------- -----------
| ------ B7 ------- |
| ---------- ----------- B8 |
| ---- B9 ---- |
| ---- ----- B10 |
| -------- -------- B11 |
| ---- ---- B12 |
| ---- ---- B13 |
---------------------- | ------- A ------- | ----------- ----------- ```

``` AND（
（STARTDATE和ENDDATE之间的'start_date'） - 迎合内部和结束date外部
要么
（'end_date'在STARTDATE和ENDDATE之间） - 迎合内部和开始date外部
要么
（STARTDATE在'start_date'和'end_date'之间） - 只有一个date在里面的外部范围需要。
）
```

` `// Current row dates var dateStart = moment("2014-08-01", "YYYY-MM-DD"); var dateEnd = moment("2014-08-30", "YYYY-MM-DD"); // Check with dates above var rangeUsedStart = moment("2014-08-02", "YYYY-MM-DD"); var rangeUsedEnd = moment("2014-08-015", "YYYY-MM-DD"); // Range covers other ? if((dateStart <= rangeUsedStart) && (rangeUsedEnd <= dateEnd)) { return false; } // Range intersects with other start ? if((dateStart <= rangeUsedStart) && (rangeUsedStart <= dateEnd)) { return false; } // Range intersects with other end ? if((dateStart <= rangeUsedEnd) && (rangeUsedEnd <= dateEnd)) { return false; } // All good return true;` `

` `(Startdate BETWEEN '".\$startdate2."' AND '".\$enddate2."') //overlap: starts between start2/end2 OR (Startdate < '".\$startdate2."' AND (enddate = '0000-00-00' OR enddate >= '".\$startdate2."') ) //overlap: starts before start2 and enddate not set 0000-00-00 (still on going) or if enddate is set but higher then startdate2` `

` `someInterval.overlaps( anotherInterval )` `

# java.time＆ThreeTen-Extra

## `Interval`

`org.threeten.extra.Interval`类是方便的，但需要date – 时间矩（ `java.time.Instant`对象）而不是仅包含date的值。 因此，我们继续使用UTC中的一天中的第一个时刻来表示date。

` `Instant start = Instant.parse( "2016-01-01T00:00:00Z" ); Instant stop = Instant.parse( "2016-02-01T00:00:00Z" );` `

` `Interval interval_A = Interval.of( start , stop );` `

` `Instant start_B = Instant.parse( "2016-01-03T00:00:00Z" ); Interval interval_B = Interval.of( start_B , Duration.of( 3 , ChronoUnit.DAYS ) );` `

` `Boolean overlaps = interval_A.overlaps( interval_B );` `

• `abuts`
• `contains`
• `encloses`
• `equals`
• `isAfter`
• `isBefore`
• `overlaps`

` `A = [StartA, EndA] B = [StartB, EndB] [---- DateRange A ------] (True if StartA > EndB) [--- Date Range B -----] [---- DateRange A -----] (True if EndA < StartB) [--- Date Range B ----]` `

` `A = (StartA, EndA) B = (StartB, EndB) (---- DateRange A ------) (True if StartA >= EndB) (--- Date Range B -----) (---- DateRange A -----) (True if EndA <= StartB) (--- Date Range B ----)` `

` `A = [StartA, EndA) B = [StartB, EndB) [---- DateRange A ------) (True if StartA >= EndB) [--- Date Range B -----) [---- DateRange A -----) (True if EndA <= StartB) [--- Date Range B ----)` `

` `A = (StartA, EndA] B = (StartB, EndB] (---- DateRange A ------] (True if StartA >= EndB) (--- Date Range B -----] (---- DateRange A -----] (True if EndA <= StartB) (--- Date Range B ----]` `

` `A = [StartA, EndA) B = [StartB, EndB] [---- DateRange A ------) (True if StartA > EndB) [--- Date Range B -----] [---- DateRange A -----) (True if EndA <= StartB) [--- Date Range B ----]` `

（StartA <🞐EndB）和（EndA>🞐StartB）

` `private Boolean overlap (Timestamp startA, Timestamp endA, Timestamp startB, Timestamp endB) { return (endB == null || startA == null || !startA.after(endB)) && (endA == null || startB == null || !endA.before(startB)); }` `

` ` // Takes a list and returns all records that have overlapping time ranges. public static IEnumerable<T> GetOverlappedTimes<T>(IEnumerable<T> list, Func<T, bool> filter, Func<T,DateTime> start, Func<T, DateTime> end) { // Selects all records that match filter() on left side and returns all records on right side that overlap. var overlap = from t1 in list where filter(t1) from t2 in list where !object.Equals(t1, t2) // Don't match the same record on right side. let in1 = start(t1) let out1 = end(t1) let in2 = start(t2) let out2 = end(t2) where in1 <= out2 && out1 >= in2 let totover = GetMins(in1, out1, in2, out2) select t2; return overlap; } public static void TestOverlap() { var tl1 = new TempTimeEntry() { ID = 1, Name = "Bill", In = "1/1/08 1:00pm".ToDate(), Out = "1/1/08 4:00pm".ToDate() }; var tl2 = new TempTimeEntry() { ID = 2, Name = "John", In = "1/1/08 5:00pm".ToDate(), Out = "1/1/08 6:00pm".ToDate() }; var tl3 = new TempTimeEntry() { ID = 3, Name = "Lisa", In = "1/1/08 7:00pm".ToDate(), Out = "1/1/08 9:00pm".ToDate() }; var tl4 = new TempTimeEntry() { ID = 4, Name = "Joe", In = "1/1/08 3:00pm".ToDate(), Out = "1/1/08 8:00pm".ToDate() }; var tl5 = new TempTimeEntry() { ID = 1, Name = "Bill", In = "1/1/08 8:01pm".ToDate(), Out = "1/1/08 8:00pm".ToDate() }; var list = new List<TempTimeEntry>() { tl1, tl2, tl3, tl4, tl5 }; var overlap = GetOverlappedTimes(list, (TempTimeEntry t1)=>t1.ID==1, (TempTimeEntry tIn) => tIn.In, (TempTimeEntry tOut) => tOut.Out); Console.WriteLine("\nRecords overlap:"); foreach (var tl in overlap) Console.WriteLine("Name:{0} T1In:{1} T1Out:{2}", tl.Name, tl.In, tl.Out); Console.WriteLine("Done"); /* Output: Records overlap: Name:Joe T1In:1/1/2008 3:00:00 PM T1Out:1/1/2008 8:00:00 PM Name:Lisa T1In:1/1/2008 7:00:00 PM T1Out:1/1/2008 9:00:00 PM Done */ }` `

` `CREATE FUNCTION IsOverlapDates ( @startDate1 as datetime, @endDate1 as datetime, @startDate2 as datetime, @endDate2 as datetime ) RETURNS int AS BEGIN DECLARE @Overlap as int SET @Overlap = (SELECT CASE WHEN ( (@startDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and end date outer OR (@endDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and start date outer OR (@startDate2 BETWEEN @startDate1 AND @endDate1) -- only one needed for outer range where dates are inside. ) THEN 1 ELSE 0 END ) RETURN @Overlap END GO --Execution of the above code DECLARE @startDate1 as datetime DECLARE @endDate1 as datetime DECLARE @startDate2 as datetime DECLARE @endDate2 as datetime DECLARE @Overlap as int SET @startDate1 = '2014-06-01 01:00:00' SET @endDate1 = '2014-06-01 02:00:00' SET @startDate2 = '2014-06-01 01:00:00' SET @endDate2 = '2014-06-01 01:30:00' SET @Overlap = [dbo].[IsOverlapDates] (@startDate1, @endDate1, @startDate2, @endDate2) SELECT Overlap = @Overlap` `
` `public static class NumberExtensionMethods { public static Boolean IsBetween(this Int64 value, Int64 Min, Int64 Max) { if (value >= Min && value <= Max) return true; else return false; } public static Boolean IsBetween(this DateTime value, DateTime Min, DateTime Max) { Int64 numricValue = value.Ticks; Int64 numericStartDate = Min.Ticks; Int64 numericEndDate = Max.Ticks; if (numricValue.IsBetween(numericStartDate, numericEndDate) ) { return true; } return false; } } public static Boolean IsOverlap(DateTime startDate1, DateTime endDate1, DateTime startDate2, DateTime endDate2) { Int64 numericStartDate1 = startDate1.Ticks; Int64 numericEndDate1 = endDate1.Ticks; Int64 numericStartDate2 = startDate2.Ticks; Int64 numericEndDate2 = endDate2.Ticks; if (numericStartDate2.IsBetween(numericStartDate1, numericEndDate1) || numericEndDate2.IsBetween(numericStartDate1, numericEndDate1) || numericStartDate1.IsBetween(numericStartDate2, numericEndDate2) || numericEndDate1.IsBetween(numericStartDate2, numericEndDate2)) { return true; } return false; } if (IsOverlap(startdate1, enddate1, startdate2, enddate2)) { Console.WriteLine("IsOverlap"); }` `

` ` public static boolean checkTimeOverlaps(Date startDate1, Date endDate1, Date startDate2, Date endDate2) { if (startDate1 == null || endDate1 == null || startDate2 == null || endDate2 == null) return false; if ((startDate1.getTime() <= endDate2.getTime()) && (startDate2.getTime() <= endDate1.getTime())) return true; return false; }` `

@Bretana提供的math解决scheme是好的，但忽略了两个具体细节：

1. closures或半开放的时间段
2. 空的间隔

（StartA <= EndB）和（EndA> = StartB）

（StartA <EndB）和（EndA> StartB）

` `MomentInterval a = MomentInterval.between(Instant.now(), Instant.now().plusSeconds(2)); MomentInterval b = a.collapse(); // make b an empty interval out of a System.out.println(a); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:13,909000000Z) System.out.println(b); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:11,909000000Z)` `

` `System.out.println( "startA < endB: " + a.getStartAsInstant().isBefore(b.getEndAsInstant())); // false System.out.println( "endA > startB: " + a.getEndAsInstant().isAfter(b.getStartAsInstant())); // true System.out.println("a overlaps b: " + a.intersects(b)); // a overlaps b: false` `

` `if (StartDate1 > StartDate2) swap(StartDate, EndDate); (StartDate1 <= EndDate2) and (StartDate2 <= EndDate1);` `

“两个date范围相交”的情况包含两种情况 – 第一个date范围在第二个范围内开始，或者第二个date范围在第一个范围内开始。

` `//custom date for example \$d1 = new DateTime("2012-07-08"); \$d2 = new DateTime("2012-07-11"); \$d3 = new DateTime("2012-07-08"); \$d4 = new DateTime("2012-07-15"); //create a date period object \$interval = new DateInterval('P1D'); \$daterange = iterator_to_array(new DatePeriod(\$d1, \$interval, \$d2)); \$daterange1 = iterator_to_array(new DatePeriod(\$d3, \$interval, \$d4)); array_map(function(\$v) use (\$daterange1) { if(in_array(\$v, \$daterange1)) print "Bingo!";}, \$daterange);` `

This was my solution, it returns true when the values don't overlap:

X START 1 Y END 1

A START 2 B END 2

` `TEST1: (X <= A || X >= B) && TEST2: (Y >= B || Y <= A) && TEST3: (X >= B || Y <= A) X-------------Y A-----B TEST1: TRUE TEST2: TRUE TEST3: FALSE RESULT: FALSE --------------------------------------- X---Y A---B TEST1: TRUE TEST2: TRUE TEST3: TRUE RESULT: TRUE --------------------------------------- X---Y A---B TEST1: TRUE TEST2: TRUE TEST3: TRUE RESULT: TRUE --------------------------------------- X----Y A---------------B TEST1: FALSE TEST2: FALSE TEST3: FALSE RESULT: FALSE` `

For ruby I also found this:

` `class Interval < ActiveRecord::Base validates_presence_of :start_date, :end_date # Check if a given interval overlaps this interval def overlaps?(other) (start_date - other.end_date) * (other.start_date - end_date) >= 0 end # Return a scope for all interval overlapping the given interval, including the given interval itself named_scope :overlapping, lambda { |interval| { :conditions => ["id <> ? AND (DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", interval.id, interval.end_date, interval.start_date] }} end` `

Found it here with nice explaination -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails

Below query gives me the ids for which the supplied date range (start and end dates overlaps with any of the dates (start and end dates) in my table_name

` `select id from table_name where (START_DT_TM >= 'END_DATE_TIME' OR (END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))` `

The answer is too simple for me so I have created a more generic dynamic SQL statement which checks to see if a person has any overlapping dates.

` `SELECT DISTINCT T1.EmpID FROM Table1 T1 INNER JOIN Table2 T2 ON T1.EmpID = T2.EmpID AND T1.JobID <> T2.JobID AND ( (T1.DateFrom >= T2.DateFrom AND T1.dateFrom <= T2.DateTo) OR (T1.DateTo >= T2.DateFrom AND T1.DateTo <= T2.DateTo) OR (T1.DateFrom < T2.DateFrom AND T1.DateTo IS NULL) ) AND NOT (T1.DateFrom = T2.DateFrom)` `

I had a situation where we had dates instead of datetimes, and the dates could overlap only on start/end. 示例如下：

(Green is the current interval, blue blocks are valid intervals, red ones are overlapping intervals).

` ` (startB <= startA && endB > startA) || (startB >= startA && startB < endA)` `
` `compare the two dates: A = the one with smaller start date, B = the one with bigger start date if(A.end < B.start) return false return true` `