Tentukan Apakah Dua Tanggal Berkisar Tumpang tindih

1251

Diberi dua rentang tanggal, apa cara paling sederhana atau paling efisien untuk menentukan apakah dua rentang tanggal tumpang tindih?

Sebagai contoh, misalkan kita memiliki rentang yang ditandai oleh variabel DateTime StartDate1ke EndDate1 dan StartDate2 ke EndDate2.

Ian Nelson
sumber
3
Sangat mirip dengan stackoverflow.com/questions/306316/…
Charles Bretana
@CharlesBretana terima kasih untuk itu, Anda benar - itu hampir seperti versi dua dimensi dari pertanyaan saya!
Ian Nelson
2
sangat mirip dengan stackoverflow.com/questions/117962/...
Steven A. Lowe
2
Bagilah situasi 'dua rentang tanggal berpotongan' menjadi kasus (ada dua) kemudian uji untuk setiap kasus.
Kolonel Panic
1
Kode ini berfungsi dengan baik. Anda dapat melihat jawaban saya di sini: stackoverflow.com/a/16961719/1534785
Jeyhun Rahimov

Jawaban:

2290

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

Bukti:
Biarkan KondisiA Berarti DateRange A Lengkap Setelah DateRange B
_ |---- DateRange A ------| |---Date Range B -----| _
(Benar jika StartA > EndB)

Biarkan ConditionB Berarti DateRange A Sepenuhnya Sebelum DateRange B
|---- DateRange A -----| _ _ |---Date Range B ----|
(Benar jika EndA < StartB)

Kemudian Tumpang tindih ada jika Baik A maupun B tidak benar -
(Jika satu rentang tidak sepenuhnya setelah yang lain,
atau sepenuhnya sebelum yang lain, maka mereka harus tumpang tindih.)

Sekarang salah satu hukum De Morgan mengatakan bahwa:

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

Yang diterjemahkan menjadi: (StartA <= EndB) and (EndA >= StartB)


CATATAN: Ini termasuk kondisi di mana tepinya tumpang tindih. Jika Anda ingin mengecualikan itu,
ubah >=operator menjadi >, dan <= ke<


CATATAN 2. Berkat @Baodad, lihat blog ini , tumpang tindih sebenarnya paling:
{ endA-startA, endA - startB, endB-startA, endB - startB}

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


CATATAN 3. Berkat @tomosius, versi yang lebih singkat berbunyi:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Ini sebenarnya adalah jalan pintas sintaksis untuk implementasi yang lebih lama, yang mencakup pemeriksaan tambahan untuk memverifikasi bahwa tanggal mulai adalah pada atau sebelum tanggal akhir. Mendapat ini dari atas:

Jika tanggal mulai dan berakhir dapat rusak, yaitu, jika mungkin itu startA > endAatau startB > endB, maka Anda juga harus memeriksa apakah mereka dalam urutan, sehingga Anda harus menambahkan dua aturan validitas tambahan:
(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB) atau:
(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB) atau,
(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB)) atau:
(Max(StartA, StartB) <= Min(EndA, EndB)

Tetapi untuk mengimplementasikan Min()dan Max(), Anda harus kode, (menggunakan C ternary untuk keseness) ,:
(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)

Charles Bretana
sumber
29
Ini adalah logika yang disederhanakan berdasarkan pada dua asumsi ini: 1) StartA <EndA; 2) StartB <EndB. Tampaknya jelas tetapi dalam kenyataannya data dapat berasal dari sumber yang tidak diketahui seperti input pengguna atau database tanpa sanitasi. Ingatlah bahwa Anda perlu memvalidasi data input untuk memastikan kedua asumsi tersebut benar sebelum Anda dapat menggunakan logika yang disederhanakan ini atau semuanya akan berantakan. Pelajaran dari pengalaman saya sendiri;)
Devy
12
@ Evy, Anda benar. Kecuali itu juga akan berfungsi jika startA = endA. Memang, itulah tepatnya kata Startdan Endartinya. Jika Anda memiliki dua variabel bernama Atas dan Bawah, atau Timur dan Barat, atau HighValue dan LoValue, dapat diasumsikan atau tersirat bahwa sesuatu atau seseorang, di suatu tempat harus memastikan bahwa salah satu pasangan nilai tidak disimpan dalam variabel yang berlawanan. -Hanya satu dari dua pasangan karena, yah, itu juga akan berfungsi jika kedua pasangan nilai diaktifkan.
Charles Bretana
15
Anda dapat dengan mudah menambahkan nullable startdan end(dengan semantik yang "null start" = "Dari awal waktu" dan "null end" = "Sampai akhir waktu") seperti itu:(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
Kevin Robatel
9
Jawaban terbaik di Stackexchange! Senang rasanya melihat penjelasan mengapa formula pintar ini bekerja!
Abeer Sul
4
Ini adalah bentuk paling ringkas yang dapat saya pikirkan, yang juga mengembalikan false jika input tidak valid (tanggal mulai> = tanggal akhir)DateRangesOverlap = max(start1, start2) < min(end1, end2)
tomosius
406

Saya percaya bahwa cukup untuk mengatakan bahwa kedua rentang tumpang tindih jika:

(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)
Ian Nelson
sumber
76
Saya menemukan (StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)notasi lebih mudah dimengerti, Range1 selalu di sebelah kiri dalam tes.
AL
8
Ini mengasumsikan tanggal mulai dan berakhir sudah termasuk. Ubah <=ke <jika mulai inklusif dan akhir eksklusif.
Richard Schneider
Ini akan bekerja dengan sangat baik bahkan jika startDate2 adalah sebelum startDate1. Jadi tidak perlu berasumsi bahwa startDate1 lebih awal dari startDate2.
Shehan Simen
3
Saya menemukan (StartDate1 <= EndDate2) dan notasi (StartDate2 <= EndDate1) (sesuai jawaban) lebih mudah dipahami daripada di jawaban lain.
apc
Bagaimana cara mengadaptasi sehingga berfungsi dengan data yang memiliki StartDate1 AND / OR EndDate1? Kode ini mengasumsikan bahwa StartDate1 dan EndDate1 selalu ada. Bagaimana jika StartDate1 diberikan tetapi tidak ada EndDate1 ATAU EndDate1 diberikan tetapi tidak StartDate1. Bagaimana cara menangani kasing ekstra ini?
juFo
117

Artikel ini Perpustakaan Periode Waktu untuk .NET menjelaskan hubungan dua periode waktu dengan enumerasi PeriodRelation :

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

masukkan deskripsi gambar di sini


sumber
Bagus, saya telah menerapkan aljabar interval Allens di Jawa, juga, lihat API IntervalRelation dan IsoInterval
Meno Hochschild
80

Untuk alasan tentang hubungan temporal (atau hubungan interval lainnya, lihat itu), pertimbangkan Aljabar Interval Allen . Ini menggambarkan 13 kemungkinan hubungan yang dapat dimiliki oleh dua interval satu sama lain. Anda dapat menemukan referensi lain - "Interval Allen" tampaknya merupakan istilah pencarian operatif. Anda juga dapat menemukan informasi tentang operasi ini di Mengembangkan Aplikasi Berorientasi Waktu Snodgrass dalam SQL (PDF tersedia online di URL), dan dalam Date, Darwen dan Lorentzos Data Temporal dan Model Relasional (2002) atau Teori Waktu dan Relasional: Database Temporal di Model Relasional dan SQL (2014; efektif TD&RM edisi kedua).


Jawaban singkat (ish) adalah: diberi dua interval tanggal Adan Bdengan komponen .startdan .endkendala .start <= .end, kemudian dua interval tumpang tindih jika:

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

Anda dapat mengatur penggunaan >=vs >dan <=vs <untuk memenuhi persyaratan Anda untuk tingkat tumpang tindih.


Komentar dari ErikE:

Anda hanya bisa mendapatkan 13 jika Anda menghitung hal-hal lucu ... Saya bisa mendapatkan "15 hubungan yang mungkin dimiliki dua interval" ketika saya menjadi gila dengan itu. Dengan penghitungan yang masuk akal, saya hanya mendapat enam, dan jika Anda membuang peduli apakah A atau B lebih dulu, saya hanya mendapatkan tiga (tidak berpotongan, sebagian berpotongan, satu sepenuhnya dalam lainnya). 15 berbunyi seperti ini: [sebelum: sebelum, mulai, di dalam, akhir, setelah], [mulai: mulai, di dalam, akhir, setelah], [dalam: di dalam, akhir, setelah], [akhir: akhir, setelah], [ setelah: setelah].

Saya pikir Anda tidak dapat menghitung dua entri 'sebelum: sebelum' dan 'setelah: setelah'. Saya bisa melihat 7 entri jika Anda menyamakan beberapa hubungan dengan inversnya (lihat diagram dalam URL Wikipedia yang direferensikan; ia memiliki 7 entri, 6 di antaranya memiliki invers berbeda, dengan yang sama dengan tidak memiliki invers berbeda). Dan apakah ketiganya masuk akal tergantung pada kebutuhan Anda.

----------------------|-------A-------|----------------------
    |----B1----|
           |----B2----|
               |----B3----|
               |----------B4----------|
               |----------------B5----------------|
                      |----B6----|
----------------------|-------A-------|----------------------
                      |------B7-------|
                      |----------B8-----------|
                         |----B9----|
                         |----B10-----|
                         |--------B11--------|
                                      |----B12----|
                                         |----B13----|
----------------------|-------A-------|----------------------
Jonathan Leffler
sumber
1
Anda hanya bisa mendapatkan 13 jika Anda menghitung hal-hal lucu ... Saya bisa mendapatkan "15 hubungan yang mungkin dimiliki dua interval" ketika saya menjadi gila dengan itu. Dengan penghitungan yang masuk akal, saya hanya mendapat enam, dan jika Anda membuang peduli apakah A atau B lebih dulu, saya hanya mendapatkan tiga (tidak berpotongan, sebagian berpotongan, satu sepenuhnya dalam lainnya). 15 berbunyi seperti ini: [sebelum: sebelum, mulai, di dalam, akhir, setelah], [mulai: mulai, di dalam, akhir, setelah], [dalam: di dalam, akhir, setelah], [akhir: akhir, setelah], [ setelah: setelah].
ErikE
@ Emtucifor: Saya pikir Anda tidak dapat menghitung dua entri 'sebelum: sebelum' dan 'setelah: setelah'.
Jonathan Leffler
Re pembaruan Anda: B1 ke A adalah sebelum: sebelum dan B13 ke A adalah setelah: setelah. Diagram bagus Anda tidak memiliki awal: mulai antara B5 B6, dan ujung: akhir antara B11 dan B12. Jika berada di titik akhir adalah signifikan, maka Anda harus menghitungnya, sehingga penghitungan akhir adalah 15, bukan 13. Saya tidak berpikir bahwa titik akhir itu signifikan, jadi saya pribadi menghitungnya [sebelum: sebelum, dalam, setelah] , [di dalam: di dalam, setelah], [setelah: setelah] yang datang ke 6. Saya pikir seluruh titik akhir hanya kebingungan tentang apakah batas tersebut inklusif atau eksklusif. Eksklusivitas titik akhir tidak mengubah hubungan inti!
ErikE
Yaitu, dalam skema saya ini setara: (B2, B3, B4), (B6, B7, B9, B10), (B8, B11, B12). Saya menyadari bahwa B7 menyiratkan informasi bahwa kedua rentang tepat bertepatan. Tapi saya tidak yakin informasi tambahan ini harus menjadi bagian dari hubungan persimpangan dasar. Sebagai contoh, ketika dua interval adalah panjang yang sama persis bahkan jika tidak kebetulan atau bahkan tumpang tindih, haruskah itu dianggap sebagai "hubungan" lain? Saya bilang tidak, dan melihat aspek tambahan ini adalah satu-satunya yang membuat B7 berbeda dari B6, maka saya pikir memiliki titik akhir-sebagai-kasus terpisah membuat hal-hal tidak konsisten.
ErikE
@ Emtucifor: Oke - Saya mengerti mengapa saya salah mengidentifikasi 'sebelum: sebelum' dan 'sesudah: setelah' sebagai entri; namun, saya tidak bisa menggambarkan seperti apa entri 'mulai: mulai' dan 'ujung: ujung'. Karena Anda tidak dapat mengedit diagram saya, dapatkah Anda mengirim email kepada saya (lihat profil saya) dengan salinan diagram yang dimodifikasi yang memperlihatkan hubungan 'mulai: mulai' dan 'ujung: ujung'? Saya tidak memiliki masalah besar dengan pengelompokan Anda.
Jonathan Leffler
30

Jika tumpang tindih itu sendiri juga harus dihitung, Anda dapat menggunakan rumus berikut:

overlap = max(0, min(EndDate1, EndDate2) - max(StartDate1, StartDate2))
if (overlap > 0) { 
    ...
}
Vitalii Fedorenko
sumber
jadi tumpang tindih adalah jumlah waktu yang dibagikan kedua acara? Apakah ini berfungsi untuk semua cara yang berbeda sehingga acara dapat tumpang tindih?
NSjonas
18

Semua solusi yang memeriksa banyak kondisi berdasarkan di mana rentang tersebut terkait satu sama lain dapat sangat disederhanakan dengan hanya memastikan bahwa rentang tertentu dimulai lebih awal! Anda memastikan bahwa rentang pertama dimulai lebih awal (atau pada saat yang sama) dengan menukar kisaran jika perlu di depan.

Kemudian, Anda dapat mendeteksi tumpang tindih jika mulai rentang lainnya kurang dari atau sama dengan akhir rentang pertama (jika rentang inklusif, mengandung waktu mulai dan akhir) atau kurang dari (jika rentang termasuk awal dan eksklusif akhir) .

Dengan asumsi inklusif di kedua ujungnya, hanya ada empat kemungkinan yang salah satunya adalah tumpang tindih:

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

Titik akhir rentang 2 tidak masuk ke dalamnya. Jadi, dalam pseudo-code:

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

Ini bisa disederhanakan menjadi:

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

Jika rentang inklusif di awal dan eksklusif di akhir, Anda hanya perlu mengganti >dengan >=dalam ifpernyataan kedua (untuk segmen kode pertama: di segmen kode kedua, Anda akan menggunakan <daripada <=):

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

Anda sangat membatasi jumlah cek yang harus Anda lakukan karena Anda menghapus setengah dari ruang masalah lebih awal dengan memastikan rentang 1 tidak pernah dimulai setelah rentang 2.

paxdiablo
sumber
2
+1 untuk menyebutkan masalah inklusif / eksklusif. Saya akan memberikan jawaban sendiri ketika saya punya waktu, tetapi tidak perlu sekarang. Masalahnya adalah Anda hampir tidak pernah membiarkan awal dan akhir inklusif secara bersamaan. Dalam industri saya itu adalah praktik umum untuk memperlakukan awal sebagai eksklusif dan akhirnya inklusif, tetapi bagaimanapun juga baik-baik saja selama Anda tetap konsisten. Ini adalah jawaban pertama yang sepenuhnya benar untuk pertanyaan ini sejauh ini ... IMO.
Brian Gideon
14

Berikut ini adalah solusi lain menggunakan JavaScript. Spesialisasi dari solusi saya:

  • Menangani nilai nol sebagai tak terhingga
  • Diasumsikan bahwa batas bawah bersifat inklusif dan batas atas eksklusif.
  • Hadir dengan banyak tes

Tes didasarkan pada bilangan bulat tetapi karena objek tanggal dalam JavaScript sebanding, Anda bisa melempar dua objek tanggal juga. Atau Anda bisa melempar cap waktu milidetik.

Kode:

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

Tes:

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

Hasil saat dijalankan dengan karma & melati & PhantomJS:

PhantomJS 1.9.8 (Linux): Dieksekusi 20 dari 20 SUKSES (0,003 dtk / 0,004 dtk)

yankee
sumber
9

Saya akan lakukan

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

Di mana IsBetweenada sesuatu seperti

    public static bool IsBetween(this DateTime value, DateTime left, DateTime right) {
        return (value > left && value < right) || (value < left && value > right);
    }
Bob
sumber
Saya lebih suka (kiri <value && value <kanan) || (kanan <nilai && nilai <kiri) untuk metode ini.
Patrick Huizinga
Terima kasih untuk ini. Membuat segalanya lebih mudah di kepalaku.
sshow
1
Mengapa Anda memeriksa empat kondisi saat Anda hanya perlu memeriksa dua syarat? Gagal.
ErikE
3
Ah, maaf, saya melihat sekarang bahwa Anda mengizinkan rentang berada dalam urutan terbalik (StartDateX> EndDateX). Aneh. Bagaimanapun, bagaimana jika StartDate1 kurang dari StartDate2 dan EndDate1 lebih besar dari EndDate2? Kode yang Anda berikan tidak akan mendeteksi kondisi yang tumpang tindih ini.
ErikE
3
Bukankah ini akan kembali salah jika Date1 berisi seluruh Date2? Kemudian StartDate1 adalah sebelum StartDate2 dan EndDate1 adalah setelah EndDate2
user158037
9

masukkan deskripsi gambar di sini

Berikut adalah kode yang melakukan keajaiban:

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

Dimana..

  • A -> 1Mulai
  • B -> 1End
  • C -> 2Mulai
  • D -> 2End

Bukti? Lihat inti kode konsol pengujian ini .

sandeep talabathula
sumber
Itu bekerja, tetapi saya lebih suka untuk menguji tidak tumpang tindih, hanya dua skenario
John Albert
Terima kasih telah menjelaskan ini menggunakan gambar. Jawaban Anda adalah solusi sempurna untuk pertanyaan ini.
Rakesh Verma
8

Inilah solusi saya di Jawa , yang bekerja pada interval tidak terbatas juga

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));
}
Khaled.K
sumber
Saya pikir maksud Anda adalah ujung yang tidak terikat alih-alih interval terbuka.
Henrik
@Henrik kedua istilah tersebut berfungsi en.wikipedia.org/wiki/Interval_(mathematics)#Terminology
Khaled.K
!startA.after(endB)berarti startA <= endB dan !endA.before(startB)berarti startB <= endA. Ini adalah kriteria untuk interval tertutup dan bukan interval terbuka.
Henrik
@Henrik benar, dan kondisi lainnya seperti endB == nulldan startA == nullperiksa interval terbuka.
Khaled.K
1
endB == null, startA == null, endA == nullDan startB == nullsemua kriteria untuk memeriksa selang waktu tak terbatas dan tidak interval terbuka. Contoh untuk perbedaan antara interval tidak terikat dan terbuka: (10, 20) dan (20, null) adalah dua interval terbuka yang tidak tumpang tindih. Yang terakhir memang memiliki akhir tanpa batas. Fungsi Anda akan kembali benar, tetapi interval tidak tumpang tindih, karena interval tidak termasuk 20. (nomor yang digunakan bukan cap waktu untuk kesederhanaan)
Henrik
7

Solusi yang diposting di sini tidak berfungsi untuk semua rentang yang tumpang tindih ...

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

solusi kerja saya adalah:

DAN (
  ('start_date' ANTARA STARTDATE DAN ENDDATE) - melayani bagian luar dan akhir tanggal
  ATAU
  ('end_date' ANTARA STARTDATE DAN ENDDATE) - melayani bagian dalam dan luar mulai tanggal
  ATAU
  (STARTDATE ANTARA 'start_date' AND 'end_date') - hanya diperlukan satu untuk rentang luar di mana tanggal berada di dalamnya.
) 
di_
sumber
5

Ini adalah solusi javascript saya dengan moment.js:

// 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;
Ignacio Pascual
sumber
4

Cara mudah untuk mengingat solusinya adalah
min(ends)>max(starts)

Radacina
sumber
3

Di Microsoft SQL SERVER - Fungsi 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
Prasenjit Banerjee
sumber
3

yang paling sederhana

Cara paling sederhana adalah dengan menggunakan perpustakaan khusus yang dirancang dengan baik untuk pekerjaan waktu.

someInterval.overlaps( anotherInterval )

java.time & ThreeTen-Extra

Yang terbaik dalam bisnis ini adalah java.timekerangka kerja yang dibangun ke Java 8 dan yang lebih baru. Tambahkan ke proyek ThreeTen-Extra yang melengkapi java.time dengan kelas tambahan, khususnya Intervalkelas yang kita butuhkan di sini.

Adapun language-agnostictag pada Pertanyaan ini, kode sumber untuk kedua proyek tersedia untuk digunakan dalam bahasa lain (perhatikan lisensinya).

Interval

The org.threeten.extra.Intervalkelas berguna, tetapi membutuhkan tanggal-waktu saat ( java.time.Instantbenda) bukan nilai tanggal-satunya. Jadi kami melanjutkan dengan menggunakan momen pertama hari itu di UTC untuk mewakili tanggal.

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

Buat Intervaluntuk mewakili rentang waktu itu.

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

Kita juga dapat mendefinisikan sebuah Intervaldengan momen awal plus a Duration.

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

Membandingkan untuk menguji tumpang tindih itu mudah.

Boolean overlaps = interval_A.overlaps( interval_B );

Anda dapat membandingkan sebuah Intervalterhadap yang lain Intervalatau Instant:

Semua ini menggunakan Half-Openpendekatan untuk menentukan rentang waktu di mana awal termasuk dan akhir adalah eksklusif .

Basil Bourque
sumber
3

Ini adalah ekstensi untuk jawaban yang sangat baik oleh @ charles-bretana

Namun jawabannya tidak membuat perbedaan antara interval terbuka, tertutup, dan setengah terbuka (atau setengah tertutup).

Kasus 1 : A, B adalah interval tertutup

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

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


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

Jika tumpang tindih: (StartA <= EndB) and (EndA >= StartB)

Kasus 2 : A, B adalah interval terbuka

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

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

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

Jika tumpang tindih: (StartA < EndB) and (EndA > StartB)

Kasus 3 : A, B langsung terbuka

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

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

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

Kondisi tumpang tindih: (StartA < EndB) and (EndA > StartB)

Kasus 4 : A, B dibiarkan terbuka

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

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

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

Kondisi tumpang tindih: (StartA < EndB) and (EndA > StartB)

Kasus 5 : Hak terbuka, B ditutup

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

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


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

Kondisi tumpang tindih: (StartA <= EndB) and (EndA > StartB)

dll ...

Akhirnya, kondisi umum untuk dua interval untuk tumpang tindih adalah

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

di mana 🞐 mengubah ketimpangan yang ketat menjadi yang tidak ketat setiap kali perbandingan dilakukan antara dua titik akhir yang disertakan.

pengguna2314737
sumber
Kasus dua, tiga, dan empat memiliki kondisi Tumpang tindih yang sama, apakah ini disengaja?
Marie
@Marie, saya baru saja mencatat beberapa kasus (tidak semua)
user2314737
Ini, tetapi sedetail jawaban Jonathan Leffler akan menjadi apa yang ada dalam pikiran saya sebagai jawaban yang diterima untuk pertanyaan OP.
mbx
3

Jawaban singkat menggunakan momentjs :

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

jawabannya didasarkan pada jawaban di atas, tetapi diperpendek.

Nitin Jadhav
sumber
2

Jika Anda menggunakan rentang tanggal yang belum berakhir (masih berjalan) misalnya tidak mengatur endDate = '0000-00-00' Anda tidak dapat menggunakan ANTARA karena 0000-00-00 bukan tanggal yang valid!

Saya menggunakan solusi ini:

(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

Jika startdate2 lebih tinggi maka enddate tidak ada tumpang tindih!

mendongkrak
sumber
2

Jawabannya terlalu sederhana bagi saya sehingga saya telah membuat pernyataan SQL dinamis yang lebih umum yang memeriksa untuk melihat apakah seseorang memiliki tanggal yang tumpang tindih.

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)
Tom McDonough
sumber
2

Solusi matematis yang diberikan oleh @Bretana baik tetapi mengabaikan dua detail spesifik:

  1. aspek interval tertutup atau setengah terbuka
  2. interval kosong

Tentang keadaan batas interval tertutup atau terbuka, solusi @Bretana berlaku untuk interval tertutup

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

dapat ditulis ulang untuk interval setengah terbuka ke:

(StartA <EndB) dan (EndA> StartB)

Koreksi ini diperlukan karena batas interval terbuka tidak termasuk dalam rentang nilai interval menurut definisi.


Dan tentang interval kosong , well, di sini hubungan yang ditunjukkan di atas tidak berlaku. Interval kosong yang tidak mengandung nilai yang valid menurut definisi harus ditangani sebagai kasus khusus. Saya mendemonstrasikannya dengan perpustakaan waktu Java saya Time4J melalui contoh ini:

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)

Braket persegi terkemuka "[" menunjukkan awal yang tertutup sementara braket terakhir ")" menunjukkan ujung terbuka.

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

Seperti yang ditunjukkan di atas, interval kosong melanggar kondisi tumpang tindih di atas (terutama startA <endB), sehingga Time4J (dan perpustakaan lain juga) harus menanganinya sebagai kasus tepi khusus untuk menjamin bahwa tumpang tindih dari setiap interval sewenang-wenang dengan interval kosong tidak ada. Tentu saja, interval tanggal (yang ditutup secara default di Time4J tetapi juga bisa setengah terbuka, seperti interval tanggal kosong) ditangani dengan cara yang sama.

Meno Hochschild
sumber
1

Berikut ini adalah metode umum yang dapat berguna secara lokal.

    // 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
         */
    }
staceyw
sumber
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");
            }
mmarjeh
sumber
3
Pikiran untuk menambahkan beberapa kata penjelasan?
Phantômaxx
1

Menggunakan Java util.Date, inilah yang saya lakukan.

    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;
    }
Shehan Simen
sumber
1

Cara termudah untuk melakukannya menurut saya adalah membandingkan apakah EndDate1 baik sebelum StartDate2 dan EndDate2 sebelum StartDate1.

Itu tentu saja jika Anda mempertimbangkan interval di mana StartDate selalu sebelum EndDate.

AlexDrenea
sumber
1

Saya memiliki situasi di mana kami memiliki tanggal alih-alih data, dan tanggal bisa tumpang tindih hanya pada awal / akhir. Contoh di bawah ini:

masukkan deskripsi gambar di sini

(Hijau adalah interval saat ini, blok biru adalah interval yang valid, yang merah adalah interval yang tumpang tindih).

Saya mengadaptasi jawaban Ian Nelson untuk solusi berikut:

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

Ini cocok dengan semua kasus yang tumpang tindih tetapi mengabaikan yang tumpang tindih yang diizinkan.

Gus
sumber
0

Bagi masalah menjadi beberapa kasus kemudian tangani setiap kasus .

Situasi 'dua rentang tanggal berpotongan' dicakup oleh dua kasus - rentang tanggal pertama dimulai dalam yang kedua, atau rentang tanggal kedua dimulai dalam yang pertama.

Kolonel Panic
sumber
0

Anda dapat mencoba ini:

//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);
Ilya
sumber
0

Ini adalah solusi saya, ini mengembalikan true ketika nilai tidak tumpang tindih:

X MULAI 1 Y AKHIR 1

A MULAI 2 B AKHIR 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
Fez Vrasta
sumber
0

Untuk ruby ​​saya juga menemukan ini:

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

Ditemukan di sini dengan penjelasan yang bagus -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails

mahatmanich
sumber
0

Kueri di bawah ini memberi saya id yang rentang tanggal yang disediakan (tanggal mulai dan berakhir tumpang tindih dengan salah satu tanggal (tanggal mulai dan berakhir) di nama_kabel saya

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