İki Tarih Aralığının Çakışıp Çakışmadığını Belirleme


1249

İki tarih aralığı göz önüne alındığında, iki tarih aralığının çakışıp çakışmadığını belirlemenin en basit veya en etkili yolu nedir?

Bir örnek olarak, DateTime değişkenler ile gösterilen aralıklar olduğunu varsayalım StartDate1için EndDate1 ve StartDate2 için EndDate2.



@CharlesBretana bunun için teşekkürler, haklısın - bu neredeyse sorumun iki boyutlu bir versiyonu gibi!
Ian Nelson


2
'İki tarih aralığının kesiştiği' durumu vakalara bölün (iki tane var) ve sonra her vaka için test edin.
Albay Panik

1
Bu kod iyi çalışıyor. Cevabımı burada görebilirsiniz: stackoverflow.com/a/16961719/1534785
Jeyhun Rahimov

Yanıtlar:


2289

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

Prova:
DateAge A'nın DateRange A'yı Tamamen AfterRagege B
_ |---- DateRange A ------| |---Date Range B -----| _
(Doğru ise StartA > EndB)

KoşulB, DateRange A'nın DateRange B'den Tamamen Önceki Olduğu anlamına gelsin
|---- DateRange A -----| _ _ |---Date Range B ----|
(eğer doğruysa EndA < StartB)

Daha sonra, Ne A Ne B doğru değilse Üst üste binme olur -
(Bir aralık ne tamamen diğerinden sonra
ne de tamamen diğerinden önce değilse , üst üste binmeleri gerekir.)

Şimdi De Morgan yasalarından biri şöyle diyor:

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

Bunun anlamı: (StartA <= EndB) and (EndA >= StartB)


NOT: Bu, kenarların tam olarak üst üste geldiği koşulları içerir. Bunu hariç tutmak istiyorsanız,
değiştirmek >=operatörlerin >ve <= karşı<


NOT2. @Baodad sayesinde bakınız bu blogu , fiili örtüşme en küçüğü:
{ endA-startA, endA - startB, endB-startA, endB - startB}

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


NOT3. @Tomosius sayesinde, daha kısa bir sürüm okur:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Bu aslında daha uzun bir uygulama için sözdizimsel bir kısayoldur ve başlangıç ​​tarihlerinin bitiş tarihlerinde veya bitiş tarihlerinden önce olduğunu doğrulamak için ekstra kontroller içerir. Bunu yukarıdan türetmek:

Başlangıç ​​ve bitiş tarihleri ​​arızalı olabilirse, yani startA > endAveya mümkünse startB > endB, bunların uygun olup olmadığını da kontrol etmeniz gerekir, böylece iki ek geçerlilik kuralı eklemeniz gerekir:
(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB) veya:
(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB) veya,
(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB)) veya:
(Max(StartA, StartB) <= Min(EndA, EndB)

Ama uygulamak Min()ve Max()kodlamak zorundasınız, (C ternary'yi terslik için kullanarak) ,:
(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)


29
Bu, şu iki varsayımı temel alan basitleştirilmiş bir mantıktır: 1) StartA <EndA; 2) StartB <EndB. Açıktır, ancak gerçekte veriler, kullanıcı girişi veya sanitizasyon olmadan bir veritabanı gibi bilinmeyen bir kaynaktan gelebilir. Bu basitleştirilmiş mantığı kullanmadan önce giriş verilerinin doğrulanması gerektiğini unutmayın; aksi takdirde her şey parçalanır. Kendi deneyimlerimden alınan ders;)
Devy

12
@Devy, haklısın. Bunun haricinde startA = endA ise da çalışır. Gerçekten de, kelimeler Startve Endanlamlar tam olarak budur . Üst ve Alt ya da Doğu ve Batı ya da Yüksek Değer ve LoValue adında iki değişkeniniz varsa, bir yerlerde ya da birisinin değer çiftlerinden birinin karşıt değişkenlerde saklanmadığından emin olması gerektiği varsayılabilir ya da ima edilebilir. -Yalnızca iki çiftten biri, çünkü her iki değer çiftinin de değiştirilmesi durumunda da çalışır.
Charles Bretana

15
Kolayca boş değer ekleyebilir startve end("null start" = "Zamanın başından itibaren" ve "null end" = "zamanın sonuna" (startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
anlamıyla

9
Stackexchange'te en iyi cevap! Bu akıllı formülün neden çalıştığına dair bir açıklama görmek iyi hissettiriyor!
Abeer Sul

4
Aklıma gelen en kompakt form, geçersiz giriş durumunda da yanlış döndürüyor (başlangıç ​​tarihi> = bitiş tarihi)DateRangesOverlap = max(start1, start2) < min(end1, end2)
tomosius 20:16

406

Aşağıdaki durumlarda iki aralığın örtüştüğünü söylemek yeterli olduğuna inanıyorum:

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

76
Ben bulmak (StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)testlerde soldaki anlamak notasyonu daha kolay, Range1 zaman.
AL

8
Bu, başlangıç ​​ve bitiş tarihlerinin dahil olduğunu varsayar. Değişim <=için <ise başından dahildir ve bitiş özeldir.
Richard Schneider

StartDate2, startDate1'den önce olsa bile bu çok iyi çalışır. Bu nedenle startDate1 öğesinin startDate2 öğesinden daha erken olduğunu varsaymanıza gerek yoktur.
Shehan Simen

3
(StartDate1 <= EndDate2) ve (StartDate2 <= EndDate1) gösterimini (yanıt başına) diğer cevaplardan daha kolay anladım.
apc

StartDate1 VE / VEYA EndDate1 içeren verilerle çalışacak şekilde nasıl uyarlanır? Kod, StartDate1 ve EndDate1'in her zaman var olduğunu varsayar. StartDate1 verilir ancak EndDate1 VEYA EndDate1 verilmez ancak StartDate1 verilmezse ne olur. Bu ekstra dava nasıl ele alınır?
juFo

117

Bu makalede , .NET için Zaman Dönemi Kitaplığı , numaralandırma Dönemi İlişkisi ile iki zaman dilimi arasındaki ilişkiyi açıklar :

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

resim açıklamasını buraya girin


Güzel, Java'da Allens aralık cebirini de uyguladım , IntervalRelation ve IsoInterval API'sına bakın
Meno

80

Zamansal ilişkiler (ya da diğer herhangi bir aralık ilişkisi) hakkında akıl yürütme için Allen'in Aralık Cebiri'ni düşünün . İki aralığın birbirine göre olabileceği 13 olası ilişkiyi açıklar. Başka referanslar da bulabilirsiniz - "Allen Interval" işlevsel bir arama terimi gibi görünüyor. Bu işlemler hakkında bilgi Snodgrass'ın SQL'de Zaman Odaklı Uygulamalar Geliştirilmesi (PDF'de çevrimiçi olarak URL'de bulunabilir) ve Tarih, Darwen ve Lorentzos Geçici Verileri ve İlişkisel Model (2002) veya Zaman ve İlişkisel Teori: İlişkisel Model ve SQL (2014; etkili bir şekilde TD&RM'nin ikinci baskısı).


Kısa (ish) yanıtı şöyledir: iki tarih aralığı Ave Bbileşenler .startile .endve kısıtlamayla birlikte .start <= .end, aşağıdaki durumlarda iki aralık çakışır:

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

Çakışma derecesi ile ilgili gereksinimlerinizi karşılamak için >=vs >ve <=vs kullanımını ayarlayabilirsiniz <.


ErikE yorum:

Sadece komik şeyler sayılırsa 13 alabilirsiniz ... Onunla delirdiğimde "iki aralığın olabileceği 15 olası ilişki" elde edebilirim. Mantıklı sayımla, sadece altı tane alırım ve A veya B'nin önce gelip gelmediğine dikkat ederseniz, sadece üç tane alırım (kesişen, kısmen kesişmeyen, tamamen diğeri içinde). 15 şu şekilde gider: [önce: önce, başla, içinde, sonunda, sonra], [start: içinde, içinde, sonunda, sonra], [içinde: içinde, sonunda, sonra], [son: son, sonra], [ sonra sonra].

İki girişi 'önce: önce' ve 'sonra: sonra' sayamayacağınızı düşünüyorum. Tersleri ile bazı ilişkileri eşitlerseniz 7 giriş görebiliyordum (referans verilen Wikipedia URL'sindeki şemaya bakın; 6'sı farklı bir tersi olan, eşit bir tersi olmayan eşitsiz 7 girişi vardır). Ve üçünün mantıklı olup olmadığı gereksinimlerinize bağlıdır.

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

1
Sadece komik şeyler sayılırsa 13 alabilirsiniz ... Onunla delirdiğimde "iki aralığın olabileceği 15 olası ilişki" elde edebilirim. Mantıklı sayımla, sadece altı tane alırım ve A veya B'nin önce gelip gelmediğine dikkat ederseniz, sadece üç tane alırım (kesişen, kısmen kesişmeyen, tamamen diğeri içinde). 15 şu şekilde gider: [önce: önce, başla, içinde, sonunda, sonra], [start: içinde, içinde, sonunda, sonra], [içinde: içinde, sonunda, sonra], [son: son, sonra], [ sonra sonra].
ErikE

@Emtucifor: İki girişi 'önce: önce' ve 'sonra: sonra' sayamayacağınızı düşünüyorum.
Jonathan Leffler

Güncellemeniz: B1 - A önce: önce ve B13 - A sonra: sonra. Güzel diyagramınızda başlangıç ​​yok: B5 B6 arasında başlangıç ​​ve bitiş: B11 ve B12 arasında bitiş. Bir son nokta üzerinde olmak önemli ise son taksitli ben değil, 13. 15 yani, o zaman, bunu saymak zorunda değilsiniz [: önce dahilinde, önce, sonra] ben şahsen saymak, böylece uç nokta şey önemli olduğunu düşünüyorum , [inside: inside, after], [after: after] 6'ya geliyor. Bence tüm son nokta şey sınırların kapsayıcı mı yoksa münhasır mı olduğuyla ilgili kafa karışıklığı. Uç noktaların münhasırlığı çekirdek ilişkileri değiştirmez!
ErikE

Yani, şemamda bunlar eşdeğerdir: (B2, B3, B4), (B6, B7, B9, B10), (B8, B11, B12). B7'nin iki aralığın tam olarak çakıştığı bilgiyi ima ettiğini fark ettim. Ancak bu ek bilgilerin temel kavşak ilişkilerinin bir parçası olması gerektiğine inanmıyorum. Örneğin, iki aralık çakışan veya çakışan olmasa bile aynı uzunluktaysa, bu başka bir "ilişki" olarak mı düşünülmelidir? Hayır diyorum ve bu ek yönü B7'yi B6'dan ayıran tek şey olarak görüyorum, o zaman ayrı durumlar olarak uç noktalara sahip olmak işleri tutarsız hale getiriyor.
ErikE

@Emtucifor: Tamam - Neden 'önce: önce' ve 'sonra: sonra' girişleri olarak yanlış tanımladığımı görüyorum; Ancak, 'start: start' ve 'end: end' girişlerinin nasıl olması gerektiğini hayal edemiyorum. Diyagramımı düzenleyemediğiniz için, 'start: start' ve 'end: end' ilişkilerini gösteren diyagramın değiştirilmiş bir kopyasıyla bana e-posta gönderebilir miyim (profilime bakın)? Gruplamalarınızla ilgili önemli bir sorunum yok.
Jonathan Leffler

30

Çakışmanın kendisi de hesaplanacaksa, aşağıdaki formülü kullanabilirsiniz:

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

Öyleyse, iki olayın paylaştığı süre nedir? Bu, olayların çakışabileceği tüm farklı yollar için çalışıyor mu?
NSjonas

18

Aralıkların birbiriyle ilişkili olduğu yere bağlı olarak çok sayıda koşulu kontrol eden tüm çözümler, sadece belirli bir aralığın daha erken başlamasını sağlayarak büyük ölçüde basitleştirilebilir ! Gerekirse aralıkları önceden değiştirerek ilk aralığın daha erken (veya aynı zamanda) başlamasını sağlarsınız.

Ardından, diğer aralık başlangıcı ilk aralık ucundan küçük veya ona eşitse (aralıklar dahilse, başlangıç ​​ve bitiş zamanlarını da içeriyorsa) veya (aralıklar başlangıç ​​dahil ve bitiş hariçse) çakışmayı tespit edebilirsiniz .

Her iki uçta kapsayıcı olduğu varsayıldığında, birinin örtüşmeyen sadece dört olasılığı vardır:

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

2 aralığının bitiş noktası buna girmez. Yani, sözde kodda:

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

Bu daha da basitleştirilebilir:

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

Aralıkları sonunda başlangıç ve Exclusive dahildir, sadece değiştirmek zorunda >olan >=ikinci if(: İkinci kod segmente, kullanmak istediğiniz ilk kod segmenti için deyimi <yerine <=):

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

Yapmanız gereken kontrol sayısını büyük ölçüde sınırlandırıyorsunuz çünkü aralık 1'in asla aralık 2'den sonra başlamamasını sağlayarak sorunlu alanın yarısını erken çıkarıyorsunuz.


2
Kapsayıcı / münhasır problemden bahsettiği için +1. Zamanım olduğunda kendime bir cevap verecektim, ama şimdi gerek yok. Mesele şu ki, hem başlangıç ​​hem de sonun aynı anda kapsayıcı olmasına asla izin vermiyorsunuz. Endüstrimde, başlangıcı münhasır ve sonunu kapsayıcı olarak ele almak yaygın bir uygulamadır, ancak tutarlı olduğunuz sürece her iki şekilde de iyidir. Bu, şu ana kadar bu soruya dair ilk tamamen doğru cevap ... IMO.
Brian Gideon

14

İşte JavaScript kullanan başka bir çözüm. Çözümümün özellikleri:

  • Boş değerleri sonsuz olarak işler
  • Alt sınırın kapsayıcı ve üst sınırın dışlayıcı olduğunu varsayar.
  • Bir sürü testle birlikte gelir

Testler tamsayılara dayanır, ancak JavaScript'teki tarih nesneleri karşılaştırılabilir olduğundan iki tarih nesnesini de atabilirsiniz. Veya milisaniye zaman damgasını atabilirsiniz.

Kod:

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

Testler:

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 ile çalıştırıldığında sonuç:

PhantomJS 1.9.8 (Linux): 20 BAŞARI 20'si (0,003 sn / 0,004 sn)


9

yapardım

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

Neye IsBetweenbenziyor

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

Ben tercih ederim (sol <değer && değeri <sağ) || (sağ <değer && değeri <sol).
Patrick Huizinga

Bunun için teşekkürler. Kafamda işleri kolaylaştırıyor.
09:05

1
Neden sadece iki tanesini kontrol etmeniz gerektiğinde dört koşulu kontrol edesiniz? Başarısız.
ErikE

3
Ah, özür dilerim, şimdi aralıkların ters sırada olmasına izin verdiğini görüyorum (StartDateX> EndDateX). Garip. Her neyse, StartDate1, StartDate2'den ve EndDate1, EndDate2'den büyükse ne olur? Verdiğiniz kod bu çakışan durumu algılamayacak.
ErikE

3
Tarih1 tüm Tarih2'yi içeriyorsa bu döndürme yanlış olmaz mı? Ardından StartDate1, StartDate2'den önce ve EndDate1, EndDate2'den sonra
user158037

9

resim açıklamasını buraya girin

İşte sihri yapan kod:

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

Nerede..

  • A -> 1 Başlangıç
  • B -> 1.
  • C -> 2 Başlangıç
  • D -> 2Son

Kanıt? Bu test konsolu kod özetine bakın .


Bu işe yarıyor, ancak üst üste
John Albert

Bunu resimleri kullanarak açıkladığınız için teşekkürler. Cevabınız bu soru için mükemmel bir çözümdür.
Rakesh Verma

8

İşte Java'daki çözümüm , sınırsız aralıklarla da çalışıyor

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

Açık aralıklar yerine sınırsız sonlar demek istediğinizi düşünüyorum.
Henrik


!startA.after(endB)anlamına gelir startA <= endB ve !endA.before(startB)startB <= endA anlamına gelir. Bunlar açık bir aralık değil, kapalı bir aralık için kriterlerdir.
Henrik

Gerçek @Henrik ve diğer koşullar gibi endB == nullve startA == nullaçık aralığı için kontrol edin.
Khaled.K

1
endB == null, startA == null, endA == nullVe startB == nulltüm sınırsız bir aralığı kontrol etmek için kriterleri ve bir açık aralık vardır. Sınırsız ve açık aralıklar arasındaki farklara örnek: (10, 20) ve (20, boş) üst üste gelmeyen iki açık aralıktır. Sonuncusunun sınırsız bir sonu var. İşleviniz true değerini döndürür, ancak aralıklar çakışmaz, çünkü aralıklar 20 içermez. (Basitlik için zaman damgaları yerine kullanılan sayılar)
Henrik

7

Burada yayınlanan çözüm tüm çakışan aralıklar için işe yaramadı ...

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

benim çalışma çözümü:

VE (
  ('başlangıç_tarihi' BAŞLANGIÇ VE SON ARASINDA) - iç ve bitiş tarihi için dış
  VEYA
  ('end_date' BAŞLANGIÇ VE SON TARİH ARASINDA) - iç ve başlangıç ​​tarihi için dış
  VEYA
  (STARTDATE BETWEEN 'başlangıç_tarihi' VE 'bitiş_tarihi') - tarihlerin bulunduğu dış aralık için yalnızca bir tane gerekir.
) 

5

Bu moment.js ile benim javascript çözüm oldu:

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

4

Çözümü hatırlamanın kolay bir yolu
min(ends)>max(starts)


3

Microsoft SQL SUNUCUSU - SQL İşlevi

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

en basit

En basit yol, tarih-saat çalışması için iyi tasarlanmış özel bir kütüphane kullanmaktır.

someInterval.overlaps( anotherInterval )

java.time ve ThreeTen-Extra

İş dünyasındaki en iyisi java.timeJava 8 ve sonraki sürümlerine entegre edilmiş çerçevedir. Buna ek olarak java.time'ı ek sınıflarla, özellikle burada ihtiyacımız olan sınıfı tamamlayan ThreeTen-Extra projesine ekleyin Interval.

Bu language-agnosticSoru'daki etikete gelince , her iki projenin kaynak kodu diğer dillerde de kullanılabilir (lisanslarına dikkat edin).

Interval

org.threeten.extra.IntervalSınıf kullanışlı olmakla tarih-zaman anlar (gerektirir java.time.Instantnesneler) ziyade tarih-yalnızca değerleri. Bu nedenle, tarihi temsil etmek için günün ilk anını UTC'de kullanarak devam ediyoruz.

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

IntervalBu süreyi temsil etmek için bir oluşturun .

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

Ayrıca Intervalbaşlangıç ​​anı artı a ile bir tanımlayabiliriz Duration.

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

Çakışmaları test etmekle kıyaslamak kolaydır.

Boolean overlaps = interval_A.overlaps( interval_B );

A'yı bir Intervalbaşkasıyla karşılaştırabilir Intervalveya Instant:

Bütün bunlar Half-Open, başlangıcın dahil olduğu ve sonun münhasır olduğu bir zaman aralığının tanımlanması yaklaşımını kullanır .


3

Bu @ charles-bretana'nın mükemmel cevabının bir uzantısıdır .

Ancak cevap açık, kapalı ve yarı açık (veya yarı kapalı) aralıklar arasında bir ayrım yapmaz.

Durum 1 : A, B kapalı aralıklardır

A = [StartA, EndA]
B = [StartB, EndB]

                         [---- DateRange A ------]   (True if StartA > EndB)
[--- Date Range B -----]                           


[---- DateRange A -----]                             (True if EndA < StartB)
                         [--- Date Range B ----]

Çakışma iff: (StartA <= EndB) and (EndA >= StartB)

Durum 2 : A, B açık aralıklardır

A = (StartA, EndA)
B = (StartB, EndB)

                         (---- DateRange A ------)   (True if StartA >= EndB)
(--- Date Range B -----)                           

(---- DateRange A -----)                             (True if EndA <= StartB)
                         (--- Date Range B ----)

Çakışma iff: (StartA < EndB) and (EndA > StartB)

Durum 3 : A, B sağ açık

A = [StartA, EndA)
B = [StartB, EndB)

                         [---- DateRange A ------)   (True if StartA >= EndB) 
[--- Date Range B -----)                           

[---- DateRange A -----)                             (True if EndA <= StartB)
                         [--- Date Range B ----)

Çakışma durumu: (StartA < EndB) and (EndA > StartB)

Durum 4 : A, B açık bırakıldı

A = (StartA, EndA]
B = (StartB, EndB]

                         (---- DateRange A ------]   (True if StartA >= EndB)
(--- Date Range B -----]                           

(---- DateRange A -----]                             (True if EndA <= StartB)
                         (--- Date Range B ----]

Çakışma durumu: (StartA < EndB) and (EndA > StartB)

Durum 5 : Sağ açık, B kapalı

A = [StartA, EndA)
B = [StartB, EndB]

                         [---- DateRange A ------)    (True if StartA > EndB)
[--- Date Range B -----]                           


[---- DateRange A -----)                              (True if EndA <= StartB)  
                         [--- Date Range B ----]

Çakışma durumu: (StartA <= EndB) and (EndA > StartB)

vb...

Son olarak, iki aralığın üst üste gelmesi için genel koşul

(StartA <🞐 EndB) ve (EndA> 🞐 StartB)

burada included, dahil edilen iki uç nokta arasında karşılaştırma yapıldığında katı bir eşitsizliği katı olmayan bir eşitsizliğe dönüştürür.


İki, üç ve dördüncü vakalar aynı Çakışma durumuna sahiptir, bu kasıtlı mıdır?
Marie

@ Marie, birkaç vaka listeledim (hepsi değil)
user2314737

Bu, ama Jonathan Leffler'in cevabı kadar ayrıntılı olarak , OP sorusu için kabul edilen cevap olarak aklıma gelen şey olurdu.
mbx

3

Momentjs kullanarak kısa cevap :

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

cevap yukarıdaki cevaplara dayanmaktadır, ancak kısaltılmıştır.


2

Henüz bitmemiş (hala devam ediyor) bir tarih aralığı kullanıyorsanız, örneğin endDate = '0000-00-00' ayarlanmamışsa, 0000-00-00 geçerli bir tarih olmadığı için BETWEEN'i kullanamazsınız!

Bu çözümü kullandım:

(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 daha yüksekse, enddate çakışma olmaz!


2

Cevap benim için çok basit, bu yüzden bir kişinin çakışan tarihleri ​​olup olmadığını kontrol eden daha genel bir dinamik SQL ifadesi oluşturduk.

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 tarafından verilen matematiksel çözüm iyidir, ancak iki özel ayrıntıyı ihmal eder:

  1. kapalı veya yarı açık aralıkların görünüşü
  2. boş aralıklar

Aralık sınırlarının kapalı veya açık durumu hakkında @Bretana çözümü kapalı aralıklar için geçerlidir

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

yarı açık aralıklarla şu şekilde yeniden yazılabilir :

(StartA <EndB) ve (EndA> StartB)

Bu düzeltme gereklidir, çünkü açık aralık sınırı tanım gereği aralığın değer aralığına ait değildir.


Ve boş aralıklar hakkında , burada, yukarıda gösterilen ilişki DEĞİLDİR. Tanımı gereği geçerli bir değer içermeyen boş aralıklar özel durum olarak ele alınmalıdır. Java zaman kitaplığı Time4J tarafından bu örnek ile göstermek:

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)

Ön köşeli ayraç "[" kapalı bir başlangıcı, son köşeli ayraç ")" açık bir ucu belirtir.

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

Yukarıda gösterildiği gibi, boş aralıklar yukarıdaki örtüşme koşulunu ihlal eder (özellikle startA <endB), bu nedenle Time4J (ve diğer kütüphaneler de) boş bir aralıkla herhangi bir keyfi aralığın üst üste binmesini garanti etmek için özel kenar durumu olarak ele almalıdır. mevcut değil. Elbette, tarih aralıkları (Time4J'de varsayılan olarak kapalıdır, ancak boş tarih aralıkları gibi yarı açık olabilir) benzer bir şekilde ele alınır.


1

İşte yerel olarak yararlı olabilecek genel bir yöntem.

    // 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
Bazı açıklama kelimeleri eklemek ister misiniz?
Phantômaxx

1

Java util.Date kullanarak, burada ne yaptım.

    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

Bence bunu yapmanın en kolay yolu, EndDate1'in StartDate2'den ve EndDate2'nin StartDate1'den önce olup olmadığını karşılaştırmak olacaktır.

Tabii ki StartDate'in her zaman EndDate'ten önce olduğu aralıkları düşünüyorsanız.


1

Tarihler yerine tarihlerimizin olduğu bir durum vardı ve tarihler yalnızca başlangıç ​​/ bitiş ile çakışabilir. Aşağıdaki örnek:

resim açıklamasını buraya girin

(Yeşil geçerli aralıktır, mavi bloklar geçerli aralıklardır, kırmızı olanlar çakışan aralıklardır).

Ian Nelson'ın cevabını aşağıdaki çözüme uyarladım:

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

Bu, tüm örtüşme durumlarıyla eşleşir, ancak izin verilen örtüşme durumlarını yoksayar.


0

Sorunu vakalara bölün ve ardından her vakayı ele alın .

'İki tarih aralığının kesiştiği' durumu iki durumda ele alınır - ilk tarih aralığı ikinci içinde başlar veya ikinci tarih aralığı ilk içinde başlar.


0

Bunu deneyebilirsiniz:

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

Bu benim çözümümdü, değerler örtüşmediğinde true değerini döndürür:

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

0

Ruby için de buldum:

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

Burada güzel bir açıklama ile buldum -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails


0

Aşağıdaki sorgu bana, verilen tarih aralığının (başlangıç ​​ve bitiş tarihlerinin tablo_adımdaki tarihlerden herhangi biriyle (başlangıç ​​ve bitiş tarihleri) çakıştığı kimlikleri veriyor

select id from table_name where (START_DT_TM >= 'END_DATE_TIME'  OR   
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.