2つの日付範囲がある場合、2つの日付範囲が重複しているかどうかを判断するための最も簡単な方法または最も効率的な方法は何ですか?
例として、DateTime変数のStartDate1
からEndDate1
および StartDate2
からEndDate2
で表される範囲があるとします。
(StartA <= EndB)および(EndA> = StartB)
証明:
条件AでDateRange AがDateRange Bの後に完全に一致することを意味する_ |---- DateRange A ------| |---Date Range B -----| _
(StartA > EndB
の場合は真)
条件BでDateRange AがDateRange Bよりも完全に前であることを意味する|---- DateRange A -----| _ _ |---Date Range B ----|
(EndA < StartB
の場合は真)
AとBのどちらも真実でない場合は、重複が存在します -
(一方の範囲がもう一方の範囲を完全に超えていない場合は、
または完全に他のものよりも前では、それらは重なっていなければなりません。
De Morganの法律の1つ は次のように述べています。
Not (A Or B)
<=> Not A And Not B
これは次のように解釈されます。(StartA <= EndB) and (EndA >= StartB)
注:これには、端が正確に重なる条件が含まれます。それを除外したいのなら、>=
演算子を>
に、<=
を<
に変更
注2。 @Baodadのおかげで、 このブログ を見てください、実際のオーバーラップは少なくとものものです:
{endA-startA
、endA - startB
、endB-startA
、endB - startB
}
(StartA <= EndB) and (EndA >= StartB)
(StartA <= EndB) and (StartB <= EndA)
注3。 @tomosiusのおかげで、より短いバージョンでは、DateRangesOverlap = max(start1, start2) < min(end1, end2)
これは実際には長い実装の構文上のショートカットで、開始日が終了日より前か終了日であることを確認するための追加チェックが含まれています。上記からこれを派生させる:
開始日と終了日がずれている可能性がある場合、つまりstartA > endA
またはstartB > endB
が可能である場合は、それらが正しいことを確認する必要もあります。そのため、さらに2つの有効性規則を追加する必要があります。(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)
しかしMin()
とMax()
を実装するには、コードを書く必要があります(簡潔さのためにCの三項を使います)、(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)
以下の場合、2つの範囲が重なると言うことで十分であると思います。
(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)
この記事 .NET用期間ライブラリ 2つの期間の関係を列挙PeriodRelationで説明します。
// ------------------------------------------------------------------------
public enum PeriodRelation
{
After,
StartTouching,
StartInside,
InsideStartTouching,
EnclosingStartTouching,
Enclosing,
EnclosingEndTouching,
ExactMatch,
Inside,
InsideEndTouching,
EndInside,
EndTouching,
Before,
} // enum PeriodRelation
時間的関係(または他の任意の区間関係、そのことになること)を推論するために、 Allenの区間代数 を検討してください。それは2つの間隔が互いに関して持つことができる13の可能な関係について説明します。あなたは他の参考文献を見つけることができます - 「アレン間隔」は有効な検索語のようです。これらの操作に関する情報は、Snodgrassの SQLでの時間指向アプリケーションの開発 (オンラインのPDF URL)、およびDate、Darwen、Lorentzos 時間データとリレーショナルモデル (2002)にもあります。 時間と関係理論:関係モデルとSQLにおける時間データベース (2014;事実上TD&RMの第2版)。
短い(ish)答えは、コンポーネント.start
および.end
および制約条件.start <= .end
を含む2つの日付間隔A
およびB
が与えられた場合、2つの間隔は重なります。
A.end >= B.start AND A.start <= B.end
>=
vs >
および<=
vs <
の使用を調整して、重複の程度に関する要件を満たすことができます。
ErikEのコメント:
あなたが物事をおかしいと数えるならば、あなたは13を得ることができるだけです...私がそれに夢中になるとき、私は「2つの間隔が持つことができる15の可能な関係」を得ることができます。賢明な計算では、私は6つしか得られません、そして、あなたがAまたはBが最初に来るかどうか気にすることを捨てるならば、私は3つだけを得ます(交差しない、部分的に交差します。 [前:前、開始、範囲内、終了、後]、[開始:開始、範囲内、終了、後]、[範囲内:範囲内、終了、後]、[終了:終了、後]、[後:後]。
'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-------|----------------------
重なり自体も計算する必要がある場合は、次の式を使用できます。
overlap = max(0, min(EndDate1, EndDate2) - max(StartDate1, StartDate2))
if (overlap > 0) {
...
}
範囲が互いに関連している場所に基づいて多数の条件をチェックするすべての解決策は、特定の範囲がより早く開始されることを確認する!によって大幅に簡略化できます。同時に)必要なら前もって範囲を交換することによって。
次に、他の範囲の開始が最初の範囲の終了(範囲を含む場合は開始時間と終了時間の両方を含む)以下、または未満(範囲が開始を含み、終了を除く場合)以下の場合、オーバーラップを検出できます。 。
両端を含むと仮定すると、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
範囲が最初と最後で含まれる場合は、2番目のif
ステートメントで>
を>=
に置き換えるだけです(最初のコードセグメントの場合、2番目のコードセグメントでは、<
ではなく<=
を使用します)。
|----------------------| range 1
|---> range 2 overlap
|---> range 2 overlap
|---> range 2 no overlap
|---> range 2 no overlap
範囲1が範囲2の後に始まらないようにすることで、問題スペースの半分を早期に削除するため、チェックの数を大幅に制限できます。
これがJavaScriptを使ったもう一つの解決策です。私の解決策の特色:
テストは整数に基づいていますが、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秒)
これは言語にとらわれないものとしてタグ付けされていることを私は知っていますが、Javaで実装している皆さんのために:ホイールを再発明してJoda Timeを使用しないでください。
私はやります
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);
}
これは 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));
}
これが魔法のようなコードです。
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));
どこで..
証明?このテストをチェックしてください コンソールコードGist 。
ここに掲載されている解決策は、すべての重なっている範囲に対してうまくいきませんでした...
---------------------- | ------- A ------- | ----------- ----------- | ---- B1 ---- | | ---- B2 ---- | | - --- B3 ---- | | ---------- B4 ---------- | | ------- --------- B5 ---------------- | | ---- B6 ---- | - -------------------- | ------- A ------- | ------------- --------- | ------ B7 ------- | | ---------- B8 --- -------- | | ---- B9 ---- | | ---- B10 ----- | | - ------ B11 -------- | | ---- B12 ---- | | ---- B13 ---- | [。 ] ---------------------- | ------- A ------- | -------- --------------
私の実用的な解決策は以下のとおりです。
AND( ( 'START_DATE' BETWEEN STARTDATE AND ENDDATE) - 内側および終了日外側 OR ( 'end_date' BETWEEN STARTDATE AND ENDDATE)に対応します。 - 内側および開始日の外側 または (STARTDATE BETWEEN 'start_date'と 'end_date')を考慮 - 日付が内側の外側の範囲に必要なものは1つだけ。
これは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;
最も簡単な
最も簡単な方法は、日時作業用によく設計された専用ライブラリを使用することです。
someInterval.overlaps( anotherInterval )
ビジネスで最高のものは、Java 8以降に組み込まれた Java.time
フレームワークです。それに、Java.timeに追加のクラス、具体的にはここで必要な Interval
クラスを追加する ThreeTen-Extra プロジェクトを追加します。
この質問の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
を別の Interval
または Instant
と比較できます。
これらはすべてHalf-Open
アプローチを使用して、開始が 包括的 で終了が exclusive である期間を定義します。
答えは私には簡単すぎるので、私は人が重複する日付を持っているかどうかを確認するより一般的な動的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)
まだ終わっていない(まだ進行中の)日付範囲を使用している場合0000-00-00は有効な日付ではないため、endDate = '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よりも高い場合、重複はありません。
これは 優秀な答え @ 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 ----]
Iff:(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 ----)
Iff:(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 :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)
etc ...
最後に、2つの区間が重なる一般的な条件は、
(StartA <???? EndB)および(EndA> ???? StartB)
どこ? 2つの含まれる端点間で比較が行われるたびに、厳密な不等式を厳密でないものに変換します。
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
私の考えでは、これを行う最も簡単な方法は、EndDate1がStartDate2より前で、EndDate2がStartDate1より前かを比較することです。
もちろん、StartDateが常にEndDateより前になる間隔を検討しているのであれば。
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;
}
@Bretanaによって与えられる数学的な解決策は良いですが、2つの具体的な詳細を無視します。
区間境界の閉じた状態または開いた状態については、@ Bretanaの解法valid 閉じた区間に対して
(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(および他のライブラリも)は任意の区間と空の区間のオーバーラップを保証するために特別なEdgeケースとして扱う存在しない。もちろん、日付間隔(Time4Jではデフォルトで閉じられていますが、空の日付間隔のように半分開いていてもかまいません)も同様の方法で処理されます。
ローカルで役立つ一般的なメソッドを次に示します。
// 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
*/
}
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");
}
Rubyのために私もこれを見つけました:
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
if (StartDate1 > StartDate2) swap(StartDate, EndDate);
(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1);
問題をケースに分割してからそれぞれのケースを処理する 。
「2つの日付範囲が交差する」という状況は、2つのケースでカバーされます。1番目の日付範囲は2番目の日付範囲から始まり、2番目の日付範囲は最初の日付範囲から始まります。
覚えやすい解決策は次のようになります。min(ends)>max(starts)
入力として日付範囲を指定し、それがデータベース内の既存の日付範囲と重複しているかどうかを調べたい場合は、以下の条件が正常にあなたの要求を満たすことができます。
フォーム入力から
@StartDate
と@EndDate
を指定したとします。
条件は次のとおりです。
@StartDate
がexistingStartDate
の前でexistingEndDat
eの後ろにある場合、@StartDate
は既存の日付範囲の中央にあると言えるので、重複すると結論付けることができます。
@StartDate >=existing.StartDate And @StartDate <= existing.EndDate)
@StartDate
がexistingStartDate
より遅れているが@EndDate
がexistingStartDate
より進んでいる場合、それは重なると言えるでしょう。
(@StartDate <= existing.StartDate And @EndDate >= existing.StartDate)
@StartDate
がexistingStartDate
より遅れていて、そして@EndDate
がexistingEndDate
より進んでいる場合、提供された日付範囲は既存の日付範囲を逸脱していると結論付けることができます。
(@StartDate <= existing.StartDate And @EndDate >= existing.EndDate))
いずれかの条件が当てはまる場合、指定した日付範囲はデータベース内の既存の日付範囲と重複します。
以下のクエリは私に与えられた日付範囲(開始日と終了日)が私の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'))
簡単な解決策:
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
これを試すことができます:
//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);
これは私の解決策でした。値が重ならない場合はtrueを返します。
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
これが moment js を使ったもう一つの短い答えです。
function isOverlapping(startDate1, endDate1, startDate2, endDate2){
return moment(startDate1).isSameOrBefore(endDate2) &&
moment(startDate2).isSameOrBefore(endDate1);
}
答えは上記の答えに基づいていますが、短縮されています。