2つの日付範囲が重複しているかどうかを判断する


1250

2つの日付範囲が与えられた場合、2つの日付範囲が重複しているかどうかを判断する最も簡単または最も効率的な方法は何ですか?

例として、DateTime変数StartDate1to EndDate1 および StartDate2 toで示される範囲があるEndDate2ます。



@CharlesBretanaありがとうございます。正解です。これは、私の質問の2次元バージョンのようなものです。
Ian Nelson


2
「2つの日付範囲が交差する」状況をケースに分割し(2つあります)、各ケースについてテストします。
大佐パニック

1
このコードは正常に動作します。私の答えはここにあります: stackoverflow.com/a/16961719/1534785
Jeyhun Rahimov

回答:


2290

(StartA <= EndB)および(EndA> = StartB)

証明:
ConditionAは、DateRange Bの後にDateRange Aが完全に続くことを意味する
_ |---- DateRange A ------| |---Date Range B -----| _
(trueの場合StartA > EndB

ConditionBは、DateRange Aが完全にDateRange Bの前であることを意味します
|---- DateRange A -----| _ _ |---Date Range B ----|
(True if EndA < StartB

次に、AもBも真でない場合、重複が存在します-
(1つの範囲が完全に他の後に
も完全に前にもない場合、それらは重複する必要があります。)

現在、ドモーガンの法則の 1つは次のように述べています。

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

これは次のように変換されます: (StartA <= EndB) and (EndA >= StartB)


注:これには、エッジが正確に重なる条件が含まれます。あなたはそれを除外したい場合は、
変更>=に演算子を>、と<= します<


注2。@Baodadのおかげで、参照このブログを、実際のオーバーラップは、少なくともあります:
{ endA-startAendA - startBendB-startAendB - startB}

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


注3。@tomosiusのおかげで、短いバージョンが読み取られます。
DateRangesOverlap = max(start1, start2) < min(end1, end2)
これは実際には、より長い実装の構文上のショートカットです。これには、開始日がendDates以前であるかどうかを確認する追加のチェックが含まれます。上からこれを導きます:

開始日と終了日が順不同である可能性がある場合、つまりstartA > endAorまたはである可能性がある場合はstartB > endB、それらが正しいかどうかも確認する必要があります。つまり、次の2つの有効性ルールを追加する必要があります:
(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB) or:
(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB) or
(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB)) またはor:
(Max(StartA, StartB) <= Min(EndA, EndB)

しかし、Min()and を実装Max()するには、(簡潔にするためにCの3項を使用して)次のようにコーディングする必要があります。
(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)


29
これは、次の2つの仮定に基づく単純化されたロジックです。1)StartA <EndA; 2)StartB <EndB。当たり前のようですが、実際には、データはユーザー入力やデータベースのような未知のソースから無害化されていない可能性があります。この簡略化されたロジックを使用する前に、入力データを検証してこれら2つの仮定が真であることを確認する必要があることに注意してください。そうしないと、すべてがバラバラになります。私自身の経験から学んだ教訓;)
Devy

12
@Devy、あなたは正しいです。ただし、startA = endAの場合も機能します。実際、それがまさに言葉StartEnd意味です。TopとBottom、またはEastとWest、またはHighValueとLoValueという名前の2つの変数がある場合、どこかで、値のペアの1つが反対の変数に格納されていないことを確認する必要があります。-2つのペアのうちの1つだけです。これは、値の両方のペアを切り替えた場合にも機能するためです。
Charles Bretana、2015

15
あなたは簡単にnullable startを追加することができますend(「null start」=「時間の始まりから」および「null end」=「時間の終わりまで」という(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
セマンティック

9
Stackexchangeのベストアンサー!このスマートなフォーミュラが機能する理由についての説明を見るのは気持ちがいいです!
Abeer Sul 2016

4
これは私が考えることができる最もコンパクトな形式で、無効な入力(開始日> =終了日)の場合もfalseを返しますDateRangesOverlap = max(start1, start2) < min(end1, end2)
tomosius

406

次の場合、2つの範囲が重複していると言えば十分だと思います。

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

76
(StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)表記の方がわかりやすいと思います。テストではRange1が常に左側にあります。
ALは

8
これは、開始日と終了日が含まれていることを前提としています。開始が包括的で、終了が排他的である場合に変更<=<ます。
Richard Schneider 14

これは、startDate2がstartDate1より前であっても非常にうまく機能します。したがって、startDate1がstartDate2より前であると想定する必要はありません。
Shehan Simen、2015

3
(StartDate1 <= EndDate2)および(StartDate2 <= EndDate1)表記(回答ごと)は他の回答よりもわかりやすいことがわかりました。
apc

StartDate1および/またはEndDate1を持つデータで動作するように適応するにはどうすればよいですか?このコードは、StartDate1とEndDate1が常に存在することを前提としています。StartDate1は指定されているがEndDate1が指定されていない、またはEndDate1が指定されているがStartDate1が指定されていない場合はどうなりますか。この余分なケースをどのように処理しますか?
juFo

117

この記事.NET用の期間ライブラリでは、列挙型PeriodRelationによる2つの期間の関係について説明しています。

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

ここに画像の説明を入力してください


いいですね、Allens間隔代数もJavaに実装しました。IntervalRelationおよびIsoIntervalのAPIを
Meno Hochschild

80

時間的関係(またはその他の区間関係については、ここまで来てください)についての推論については、アレンの区間代数を検討してください。これは、2つの間隔が互いに対して持つことができる13の可能な関係を示しています。他の参考文献を見つけることができます—「Allen Interval」は有効な検索用語のようです。これらの操作に関する情報は、SnodgrassのSQLでの時間指向アプリケーション開発(PDFはURLでオンラインで利用可能)、Date、DarwenおよびLorentzosの時間データと関係モデル(2002)または 時間と関係理論:時間データベースRelational Model and SQL(2014;事実上、TD&RMの第2版)。


short(ish)の答えは次のとおりです。2つの日付間隔がAありB、コンポーネント.start.end制約.start <= .endが指定されている場合、2つの間隔は次の場合に重複します。

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

>=vs ><=vsの使用を調整して<、重複の程度に関する要件を満たすことができます。


ErikEのコメント:

面白いことを数えると13しか得られません...私がそれに夢中になったとき、私は「2つの間隔が持つことができる15の可能な関係」を得ることができます。賢明なカウントでは、6しか得られません。AとBのどちらが先かを気にかけても、3しか得られません(交差せず、部分的に交差し、1つは完全に他の中にあります)。15は次のようになります:[before:before、start、within、end、after]、[start:start、within、end、after]、[within:within、end、after]、[end:end、after]、[後:後]。

「before:before」と「after:after」の2つのエントリーは数えられないと思います。いくつかの関係をそれらの逆と同等と見なすと、7つのエントリが表示されます(参照されているWikipediaのURLの図を参照してください。7つのエントリがあり、そのうち6つは異なる逆を持ち、等しいと明確な逆はありません)。そして、3つが賢明かどうかは、要件によって異なります。

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

1
面白いことを数えると13しか得られません...私がそれに夢中になったとき、「2つの間隔が持つことができる15の可能な関係」を得ることができます。賢明なカウントでは、6しか得られません。AとBのどちらが先かを気にかけても、3しか得られません(交差せず、部分的に交差し、1つは完全に他の中にあります)。15は次のようになります:[before:before、start、within、end、after]、[start:start、within、end、after]、[within:within、end、after]、[end:end、after]、[後:後]。
ErikE

@Emtucifor:「before:before」と「after:after」の2つのエントリは数えられないと思います。
ジョナサンレフラー

更新について:B1からAはbefore:beforeで、B13からAはafter:afterです。あなたの素敵な図には、B5とB6の間のstart:startとB11とB12の間のend:endがありません。エンドポイントにいることが重要である場合、それを数える必要があるため、最終的な集計は13ではなく15 です。エンドポイントのことは重要ではないと思うので、個人的に数えます[前:前、前、後] 、[within:within、after]、[after:after]は6になります。エンドポイント全体は、境界が包括的であるか排他的であるかについての混乱であると思います。エンドポイントの排他性は、コアの関係を変更しません!
ErikE 2010年

つまり、私のスキームでは、これらは同等です(B2、B3、B4)、(B6、B7、B9、B10)、(B8、B11、B12)。B7は、2つの範囲が正確に一致するという情報を意味することを理解しています。しかし、私はこの追加情報が基本交差関係の一部であるべきだとは確信していません。たとえば、2つの間隔が一致または重複していなくても、正確に同じ長さである場合、それを別の「関係」と見なす必要がありますか?私はノーと言い、この追加の側面がB7をB6と区別する唯一のものであることを考えると、endpoints-as-separate-casesがあると状況が一貫しなくなると思います。
ErikE 2010年

@Emtucifor:OK-「before:before」と「after:after」をエントリとして誤って識別した理由がわかります。ただし、「start:start」および「end:end」エントリがどのように見えるかを想像できません。私の図は編集できないので、 'start:start'および 'end:end'関係を示す図の変更されたコピーをメールで送っていただけますか(私のプロフィールを参照)。私はあなたのグループ分けに大きな問題はありません。
ジョナサンレフラー、2010年

30

オーバーラップ自体も計算する必要がある場合は、次の式を使用できます。

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

オーバーラップは、2つのイベントが共有する時間の長さですか。これは、イベントがオーバーラップするさまざまな方法で機能しますか?
NSjonas 2016

18

範囲が相互に関連している場所に基づいて多数の条件をチェックするすべてのソリューションは、特定の範囲がより早く始まるようにするだけで大幅に​​簡略化できます必要に応じて範囲を前もって交換することにより、最初の範囲がより早く(または同時に)開始するようにします。

次に、他の範囲の開始が最初の範囲の終了以下(範囲が包括的で、開始時間と終了時間の両方を含む場合)またはより小さい(範囲に開始が含まれ、終了が含まれない場合)、オーバーラップを検出できます。 。

両端を含むと仮定すると、重複しない可能性は4つしかありません。

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

範囲2の端点は入りません。したがって、疑似コードでは:

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

範囲が最後に開始し、排他的に含まれている場合、あなただけ交換する必要が>>=第二中if(:第2のコードセグメントでは、あなたが使用したい第1のコードセグメントのための文<ではなく<=):

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

範囲1が範囲2の後に開始されないようにすることで問題のスペースの半分を早期に削除するため、実行する必要があるチェックの数を大幅に制限します。


2
包括的/排他的問題について言及する場合は+1。時間があったら自分で答えを出していましたが、今は必要ありません。問題は、開始と終了の両方が同時に含まれることをほとんど許可しないことです。私の業界では、開始を排他的、終了を包括的として扱うのが一般的ですが、一貫性を保つ限り、どちらの方法でも問題ありません。これがこれまでのところ、この質問に対する最初の完全に正しい答えです... IMO。
ブライアンギデオン

14

JavaScriptを使用したさらに別のソリューションを次に示します。私のソリューションの専門:

  • null値を無限大として処理します
  • 下限が含まれ、上限が含まれないと想定します。
  • たくさんのテストが付属しています

テストは整数に基づいていますが、JavaScriptの日付オブジェクトは同等であるため、2つの日付オブジェクトを投入することもできます。または、ミリ秒のタイムスタンプをスローすることもできます。

コード:

/**
 * 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);
}

テスト:

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);
    });
});

karma&jasmine&PhantomJSで実行した場合の結果:

PhantomJS 1.9.8(Linux):20の20の成功を実行(0.003秒/ 0.004秒)


9

私はするだろう

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);
    }

(左<値&&値<右)|| (右<値&&値<左)このメソッドの場合。
Patrick Huizinga、

これをありがとう。頭の中で物事が楽になります。
sshow 2009年

1
2つだけをチェックする必要があるのに、なぜ4つの条件をチェックするのですか?不合格。
ErikE

3
ああ、申し訳ありませんが、範囲を逆順にすることができるようになりました(StartDateX> EndDateX)。奇妙な。とにかく、StartDate1がStartDate2より小さく、EndDate1がEndDate2より大きい場合はどうなりますか?あなたが与えたコードはこの重複する状態を検出しません。
ErikE 2010年

3
Date1にDate2全体が含まれている場合、これはfalseを返しませんか?次に、StartDate1はStartDate2より前で、EndDate1はEndDate2より後です
user158037

9

ここに画像の説明を入力してください

これが魔法をかけるコードです:

 var isOverlapping =  ((A == null || D == null || A <= D) 
            && (C == null || B == null || C <= B)
            && (A == null || B == null || A <= B)
            && (C == null || D == null || C <= D));

どこ..

  • A-> 1開始
  • B->片端
  • C-> 2開始
  • D-> 2エンド

証明?このテストコンソールコードの要点を確認してください。


動作することを、私は、重複しないためのテストに唯一の2つのシナリオを好むだろう
ジョン・アルバート・

画像を使用してこれを説明していただきありがとうございます。あなたの答えは、この質問の完璧な解決策です。
Rakesh Verma

8

これがJavaの私の解決策です、無制限の間隔でも動作します

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));
}

私はあなたが開いた間隔の代わりに無限の終わりを意味したと思います。
Henrik


!startA.after(endB)startA <= endBを!endA.before(startB)意味し、startB <= endAを意味します。これらは、オープンインターバルではなく、クローズドインターバルの基準です。
Henrik

@Henrik true、およびなどのその他の条件endB == nullおよびstartA == nullオープンインターバルのチェック。
Khaled.K 2017年

1
endB == nullstartA == nullendA == nullおよびstartB == null無制限区間ではなく開区間をチェックするためのすべての基準です。無制限の間隔と開いた間隔の違いの例:(10、20)と(20、null)は、重なり合わない2つの開いた間隔です。最後のものは無限の終わりを持っています。関数はtrueを返しますが、間隔には20が含まれないため、間隔はオーバーラップしません。(単純化するために、タイムスタンプの代わりに使用された数値)
Henrik

7

ここに投稿されたソリューションは、すべての重複範囲に対して機能しませんでした...

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

私の作業解決策は:

AND(
  ( 'start_date' BETWEEN STARTDATE AND ENDDATE)-内部日付と終了日付外部に対応
  または
  ( 'end_date' BETWEEN STARTDATE AND ENDDATE)-内部および開始日の外部に対応
  または
  (STARTDATE BETWEEN 'start_date' AND 'end_date')-日付が内側にある外側の範囲に必要なのは1つだけです。
) 

5

これは、moment.jsを使用したJavaScriptソリューションでした。

// 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;


3

Microsoft SQL SERVERの場合-SQL関数

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

3

もっとも単純な

最も簡単な方法は、日付時刻の作業に適切に設計された専用ライブラリを使用することです。

someInterval.overlaps( anotherInterval )

java.timeおよびThreeTen-Extra

ビジネスで最も優れているのは、java.timeJava 8以降に組み込まれているフレームワークです。これに、java.timeを追加のクラス、特にここで必要なクラスで補足するThreeTen-Extraプロジェクトを追加しIntervalます。

language-agnosticこの質問のタグについては、両方のプロジェクトのソースコードを他の言語で使用できます(ライセンスに注意してください)。

Interval

このorg.threeten.extra.Intervalクラスは便利ですがjava.time.Instant、日付のみの値ではなく、日時の瞬間(オブジェクト)が必要です。したがって、日付を表すためにUTCでその日の最初の瞬間を使用して先に進みます。

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

Intervalその期間を表すを作成します。

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

Interval開始時刻にを加えたを定義することもできますDuration

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 );

あなたは比較することができInterval、他のに対してIntervalInstant

これらはすべてHalf-Open、開始が包括的で終了が排他的である期間を定義するアプローチを使用します。


3

これは、@ charles-bretanaによる優れた回答の拡張版です。

ただし、答えは、オープン、クローズ、ハーフオープン(またはハーフクローズ)の間隔を区別しません。

ケース1:A、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) and (EndA >= StartB)

ケース2:A、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) and (EndA > StartB)

ケース3:A、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) and (EndA > StartB)

ケース4:A、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) and (EndA > StartB)

ケース5:右開、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) and (EndA > StartB)

等...

最後に、2つの間隔が重なる一般的な条件は次のとおりです。

(StartA <🞐EndB)および(EndA>🞐StartB)

ここで、🞐は、含まれる2つのエンドポイント間で比較が行われるたびに、厳密な不等式を非厳密な不等式に変換します。


ケース2、3、および4のオーバーラップ状態が同じである場合、これは意図的なものですか?
マリー

@Marie、私はいくつかのケースをリストしました(すべてではありません)
user2314737 '22

これは、ジョナサン・レフラーの回答と同じくらい精巧なものであり、OPの質問に対する回答として受け入れられるものとして私が考えていたものです。
mbx

3

momentjsを使用した短い答え

function isOverlapping(startDate1, endDate1, startDate2, endDate2){ 
    return moment(startDate1).isSameOrBefore(endDate2) && 
    moment(startDate2).isSameOrBefore(endDate1);
}

答えは上記の答えに基づいていますが、短縮されています。


2

まだ終了していない(まだ進行中の)日付範囲を使用している場合(例:endDate = '0000-00-00'を設定しない場合)0000-00-00は有効な日付ではないため、BETWEENを使用できません!

私はこのソリューションを使用しました:

(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

startdate2がそれより大きい場合、enddateは重複しません。


2

答えは単純すぎるので、日付が重複していないかどうかを確認する、より一般的な動的SQLステートメントを作成しました。

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)

2

@Bretanaによって与えられる数学的解は良いですが、2つの特定の詳細を無視します:

  1. 閉区間または半開区間の側面
  2. 空の間隔

区間境界の閉じた状態または開いた状態について、閉じた区間に有効な@Bretanaの解

(StartA <= EndB)および(EndA> = StartB)

ハーフオープン間隔で次のように書き換えることができます。

(StartA <EndB)および(EndA> StartB)

定義では、開いている間隔の境界は間隔の値の範囲に属していないため、この修正が必要です。


そして、空の間隔については、まあ、ここで上記の関係は成り立ちません。定義上有効な値を含まない空の間隔は、特別な場合として処理する必要があります。この例を使用して、JavaタイムライブラリTime4Jでそれを示します。

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

上記のように、空の間隔は上記のオーバーラップ条件(特にstartA <endB)に違反しているため、Time4J(および他のライブラリも)は、任意の間隔と空の間隔のオーバーラップを保証するために、特別なエッジケースとして処理する必要があります。存在しません。もちろん、日付間隔(Time4Jではデフォルトで閉じられていますが、空の日付間隔のようにハーフオープンにすることもできます)も同様の方法で処理されます。


1

ローカルで役立つ一般的な方法を次に示します。

    // 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
         */
    }

1
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");
            }

3
説明の言葉を付け加えてもいいですか?
ファントマックス2014

1

Java util.Dateを使用して、ここで私がしたこと。

    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;
    }

1

私の意見では、これを行う最も簡単な方法は、EndDate1がStartDate2より前で、EndDate2がStartDate1より前かどうかを比較することです。

もちろん、StartDateが常にEndDateより前の間隔を検討している場合は、


1

日時ではなく日付があり、日付が重複するのは開始/終了時のみである状況がありました。以下の例:

ここに画像の説明を入力してください

(緑は現在の間隔、青いブロックは有効な間隔、赤いブロックは重複する間隔です)。

Ian Nelsonの回答を次の解決策に当てはめました。

   (startB <= startA && endB > startA)
|| (startB >= startA && startB < endA)

これはすべてのオーバーラップケースに一致しますが、許可されたオーバーラップケースを無視します。


0

問題をケースに分割し、各ケースを処理します

「2つの日付範囲が交差する」状況は2つのケースでカバーされます-最初の日付範囲が2番目の範囲内で始まるか、2番目の日付範囲が最初の範囲内で始まります。


0

あなたはこれを試すことができます:

//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);

0

これは私の解決策でした、値が重複しない場合はtrueを返します。

X開始1 Y終了1

Aスタート2 Bエンド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

0

ルビーの場合もこれを見つけました:

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

素晴らしい説明でここに見つかりました-> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails


0

以下のクエリは、指定された日付範囲(開始日と終了日)が私のtable_nameの日付(開始日と終了日)のいずれかと重複するIDを示します

select id from table_name where (START_DT_TM >= 'END_DATE_TIME'  OR   
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.