Xác định xem hai phạm vi ngày chồng chéo


1249

Cho hai phạm vi ngày, cách đơn giản nhất hoặc hiệu quả nhất để xác định xem hai phạm vi ngày có trùng nhau không?

Ví dụ: giả sử chúng ta có các phạm vi được biểu thị bằng các biến DateTime StartDate1đến EndDate1 StartDate2 đến EndDate2.



@CharlesBretana cảm ơn vì điều đó, bạn nói đúng - đó gần giống như một phiên bản hai chiều của câu hỏi của tôi!
Ian Nelson


2
Chia tình huống 'hai phạm vi ngày giao nhau' thành các trường hợp (có hai) sau đó kiểm tra cho từng trường hợp.
Đại tá Panic

1
Mã này hoạt động tốt. Bạn có thể xem câu trả lời của tôi ở đây: stackoverflow.com/a/16961719/1534785
Jeyhun Rahimov

Câu trả lời:


2289

(StartA <= EndB) và (EndA> = StartB)

Bằng chứng:
Đặt điều kiệnA Có nghĩa là DateRange A hoàn toàn sau DateRange B
_ |---- DateRange A ------| |---Date Range B -----| _
(Đúng nếu StartA > EndB)

Đặt điều kiệnB có nghĩa là DateRange A hoàn toàn trước DateRange B
|---- DateRange A -----| _ _ |---Date Range B ----|
(Đúng nếu EndA < StartB)

Sau đó, chồng lấp tồn tại nếu cả A hoặc B đều không đúng -
(Nếu một phạm vi không hoàn toàn theo sau phạm vi khác,
cũng không hoàn toàn trước phạm vi khác, thì chúng phải trùng nhau.)

Bây giờ một trong những luật của De Morgan nói rằng:

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

Dịch ra: (StartA <= EndB) and (EndA >= StartB)


LƯU Ý: Điều này bao gồm các điều kiện trong đó các cạnh trùng nhau chính xác. Nếu bạn muốn loại trừ rằng,
thay đổi >=nhà khai thác >, và <= để<


LƯU Ý 2. Nhờ @Baodad, xem blog này , sự chồng chéo thực tế là nhỏ nhất của:
{ endA-startA, endA - startB, endB-startA, endB - startB}

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


CHÚ THÍCH 3. Nhờ @tomosius, một phiên bản ngắn hơn có nội dung:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Đây thực sự là một phím tắt cú pháp cho việc triển khai dài hơn, bao gồm các kiểm tra bổ sung để xác minh rằng ngày bắt đầu là vào hoặc trước ngày kết thúc. Bắt nguồn từ đây:

Nếu ngày bắt đầu và ngày kết thúc có thể không theo thứ tự, nghĩa là, nếu có thể startA > endAhoặc startB > endB, thì bạn cũng phải kiểm tra xem chúng có theo thứ tự không, vì vậy điều đó có nghĩa là bạn phải thêm hai quy tắc hợp lệ bổ sung:
(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB) hoặc:
(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB) hoặc,
(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB)) hoặc:
(Max(StartA, StartB) <= Min(EndA, EndB)

Nhưng để thực hiện Min()Max(), bạn phải viết mã, (sử dụng C ternary cho sự căng thẳng) ,:
(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)


29
Đây là logic đơn giản hóa dựa trên hai giả định sau: 1) StartA <EndA; 2) Bắt đầu <EndB. Điều này có vẻ là hiển nhiên nhưng trong thực tế dữ liệu có thể đến từ nguồn không xác định như đầu vào của người dùng hoặc cơ sở dữ liệu mà không cần khử trùng. Hãy nhớ rằng bạn sẽ cần xác thực dữ liệu đầu vào để đảm bảo hai giả định đó là đúng trước khi bạn có thể sử dụng logic đơn giản này nếu không mọi thứ sẽ sụp đổ. Bài học rút ra từ kinh nghiệm của chính tôi;)
Devy

12
@Devy, Bạn đúng rồi. Ngoại trừ việc nó cũng sẽ hoạt động nếu startA = endA. Thật vậy, đó chính xác là những gì các từ StartEndý nghĩa. Nếu bạn có hai biến có tên là Top và bottom, hoặc East và West, hoặc HighValue và LoValue, có thể giả định hoặc ngụ ý rằng một cái gì đó hoặc ai đó, ở đâu đó nên đảm bảo rằng một trong các cặp giá trị không được lưu trữ trong các biến ngược lại. -Chỉ một trong hai cặp vì, tốt, nó cũng sẽ hoạt động nếu cả hai cặp giá trị được chuyển đổi.
Charles Bretana

15
Bạn có thể dễ dàng thêm nullable startend(với ngữ nghĩa là "null start" = "Từ đầu thời gian" và "null end" = "Đến cuối thời gian") như thế:(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
Kevin Robatel

9
Câu trả lời hay nhất trên Stackexchange! Cảm thấy tốt khi thấy một lời giải thích về lý do tại sao công thức thông minh này hoạt động!
Abeer Sul

4
Đây là hình thức nhỏ gọn nhất mà tôi có thể nghĩ ra, nó cũng trả về sai trong trường hợp đầu vào không hợp lệ (ngày bắt đầu> = ngày kết thúc)DateRangesOverlap = max(start1, start2) < min(end1, end2)
tomosius

406

Tôi tin rằng đủ để nói rằng hai phạm vi trùng nhau nếu:

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

76
Tôi thấy (StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)ký hiệu dễ hiểu hơn, Range1 luôn ở bên trái trong các bài kiểm tra.
AL

8
Giả định này bắt đầu và ngày kết thúc là bao gồm. Thay đổi <=thành <nếu bắt đầu là bao gồm và kết thúc là độc quyền.
Richard Schneider

Điều này sẽ hoạt động rất tốt ngay cả khi startDate2 có trước startDate1. Vì vậy, không cần phải giả sử rằng startDate1 sớm hơn startDate2.
Shehan Simen

3
Tôi thấy ký hiệu (StartDate1 <= EndDate2) và (StartDate2 <= EndDate1) (theo câu trả lời) dễ hiểu hơn so với các câu trả lời khác.
apc

Làm thế nào để thích ứng để nó hoạt động với dữ liệu có StartDate1 AND / OR EndDate1? Mã giả định rằng StartDate1 và EndDate1 luôn có mặt. Điều gì sẽ xảy ra nếu StartDate1 được đưa ra nhưng không có EndDate1 HOẶC EndDate1 nhưng không có StartDate1. Làm thế nào để xử lý trường hợp thêm này?
juFo

117

Bài viết này Thư viện khoảng thời gian cho .NET mô tả mối quan hệ của hai khoảng thời gian theo phép liệt kê periodRelation :

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

nhập mô tả hình ảnh ở đây


Thật tuyệt, tôi cũng đã triển khai đại số khoảng Allens trong Java, xem API của IntervalRelation và IsoInterval
Meno Hochschild

80

Để suy luận về các mối quan hệ tạm thời (hoặc bất kỳ mối quan hệ khoảng thời gian nào khác, hãy đến đó), xem xét Đại số Interval của Allen . Nó mô tả 13 mối quan hệ có thể có mà hai khoảng có thể có đối với nhau. Bạn có thể tìm thấy các tài liệu tham khảo khác - "Allen Interval" dường như là một thuật ngữ tìm kiếm chính thức. Bạn cũng có thể tìm thấy thông tin về các hoạt động này trong Các ứng dụng định hướng phát triển theo thời gian của Snodgrass trong SQL (PDF có sẵn trực tuyến tại URL) và trong Dữ liệu tạm thời của Darwen và Lorentzos và Mô hình quan hệ (2002) hoặc Lý thuyết thời gian và quan hệ: Cơ sở dữ liệu quan hệ Mô hình quan hệ và SQL (2014; hiệu quả là phiên bản thứ hai của TD & RM).


Câu trả lời ngắn (ish) là: đưa ra hai khoảng thời gian ABvới các thành phần .start.endràng buộc .start <= .end, sau đó hai khoảng trùng nhau nếu:

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

Bạn có thể điều chỉnh việc sử dụng >=vs ><=vs <để đáp ứng yêu cầu của bạn về mức độ chồng chéo.


Nhận xét của ErikE:

Bạn chỉ có thể nhận được 13 nếu bạn đếm những điều buồn cười ... Tôi có thể nhận được "15 mối quan hệ có thể có mà hai khoảng thời gian có thể có" khi tôi phát điên với nó. Bằng cách đếm hợp lý, tôi chỉ nhận được sáu và nếu bạn quan tâm liệu A hay B có đến trước hay không, tôi chỉ nhận được ba (không giao nhau, giao nhau một phần, một bên hoàn toàn). 15 diễn ra như sau: [trước: trước, bắt đầu, bên trong, kết thúc, sau], [bắt đầu: bắt đầu, bên trong, kết thúc, sau], [bên trong: bên trong, kết thúc, sau], [kết thúc: kết thúc, sau], [ sau nữa].

Tôi nghĩ rằng bạn không thể đếm hai mục 'trước: trước' và 'sau: sau'. Tôi có thể thấy 7 mục nếu bạn đánh đồng một số mối quan hệ với nghịch đảo của chúng (xem sơ đồ trong URL Wikipedia được tham chiếu; nó có 7 mục, 6 mục có nghịch đảo khác nhau, với bằng không có nghịch đảo khác biệt). Và liệu ba là hợp lý tùy thuộc vào yêu cầu của bạn.

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

1
Bạn chỉ có thể nhận được 13 nếu bạn đếm những điều buồn cười ... Tôi có thể nhận được "15 mối quan hệ có thể có mà hai khoảng thời gian có thể có" khi tôi phát điên với nó. Bằng cách đếm hợp lý, tôi chỉ nhận được sáu và nếu bạn quan tâm liệu A hay B có đến trước hay không, tôi chỉ nhận được ba (không giao nhau, giao nhau một phần, một bên hoàn toàn). 15 diễn ra như sau: [trước: trước, bắt đầu, bên trong, kết thúc, sau], [bắt đầu: bắt đầu, bên trong, kết thúc, sau], [bên trong: bên trong, kết thúc, sau], [kết thúc: kết thúc, sau], [ sau nữa].
ErikE

@Emtucifor: Tôi nghĩ rằng bạn không thể đếm hai mục 'trước: trước' và 'sau: sau'.
Jonathan Leffler

Cập nhật lại của bạn: B1 thành A là trước: trước và B13 đến A là sau: sau. Sơ đồ đẹp của bạn bị thiếu bắt đầu: bắt đầu giữa B5 B6 và kết thúc: kết thúc giữa B11 và B12. Nếu việc ở điểm cuối là quan trọng, thì bạn phải tính nó, vì vậy lần kiểm tra cuối cùng là 15 chứ không phải 13. Tôi không nghĩ điều cuối cùng là quan trọng, vì vậy tôi cá nhân đếm nó [trước: trước, trong, sau] , [bên trong: bên trong, sau], [sau: sau] đến 6. Tôi nghĩ rằng toàn bộ điều cuối cùng chỉ là sự nhầm lẫn về việc giới hạn là bao gồm hay độc quyền. Tính độc quyền của các điểm cuối không thay đổi các mối quan hệ cốt lõi!
ErikE

Đó là, trong sơ đồ của tôi, những cái này tương đương: (B2, B3, B4), (B6, B7, B9, B10), (B8, B11, B12). Tôi nhận ra rằng B7 ngụ ý thông tin rằng hai phạm vi hoàn toàn trùng khớp. Nhưng tôi không tin rằng thông tin bổ sung này phải là một phần của mối quan hệ giao nhau cơ sở. Ví dụ, khi hai khoảng có cùng độ dài chính xác ngay cả khi không trùng khớp hoặc thậm chí trùng nhau, liệu có nên được coi là một "mối quan hệ" khác? Tôi nói không, và xem như khía cạnh bổ sung này là điều duy nhất làm cho B7 khác biệt với B6, sau đó tôi nghĩ rằng việc có các điểm cuối như trường hợp riêng biệt làm cho mọi thứ không nhất quán.
ErikE

@Emtucifor: OK - Tôi thấy lý do tại sao tôi xác định nhầm 'trước: trước' và 'sau: sau' là các mục; tuy nhiên, tôi không thể hình dung các mục 'start: start' và 'end: end' sẽ như thế nào. Vì bạn không thể chỉnh sửa sơ đồ của tôi, bạn có thể gửi email cho tôi (xem hồ sơ của tôi) với một bản sao sửa đổi của sơ đồ hiển thị các mối quan hệ 'bắt đầu: bắt đầu' và 'kết thúc: kết thúc' không? Tôi không có vấn đề lớn với các nhóm của bạn.
Jonathan Leffler

30

Nếu tính toán trùng lặp cũng nên được tính toán, bạn có thể sử dụng công thức sau:

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

vậy sự trùng lặp là lượng thời gian mà hai sự kiện chia sẻ? Liệu điều này làm việc cho tất cả các cách khác nhau có thể chồng chéo?
NSjonas

18

Tất cả các giải pháp kiểm tra vô số điều kiện dựa trên việc các phạm vi có liên quan với nhau có thể được đơn giản hóa rất nhiều bằng cách chỉ cần đảm bảo rằng một phạm vi cụ thể bắt đầu sớm hơn! Bạn đảm bảo rằng phạm vi đầu tiên bắt đầu sớm hơn (hoặc cùng một lúc) bằng cách hoán đổi các phạm vi nếu cần thiết lên phía trước.

Sau đó, bạn có thể phát hiện sự trùng lặp nếu phạm vi bắt đầu khác nhỏ hơn hoặc bằng kết thúc phạm vi đầu tiên (nếu phạm vi được bao gồm, bao gồm cả thời gian bắt đầu và kết thúc) hoặc ít hơn (nếu phạm vi bao gồm bắt đầu và không bao gồm kết thúc) .

Giả sử bao gồm ở cả hai đầu, chỉ có bốn khả năng trong đó một khả năng không trùng lặp:

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

Điểm cuối của phạm vi 2 không nhập vào nó. Vì vậy, trong mã giả:

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

Điều này có thể được đơn giản hóa hơn nữa vào:

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

Nếu phạm vi được bao gồm ở đầu và độc quyền ở cuối, bạn chỉ cần thay thế >bằng câu lệnh >=thứ hai if(đối với đoạn mã thứ nhất: trong đoạn mã thứ hai, bạn sẽ sử dụng <thay vì <=):

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

Bạn hạn chế rất nhiều số kiểm tra bạn phải thực hiện vì bạn loại bỏ sớm một nửa không gian sự cố bằng cách đảm bảo phạm vi 1 không bao giờ bắt đầu sau phạm vi 2.


2
+1 để đề cập đến vấn đề bao gồm / độc quyền. Tôi sẽ tự mình trả lời một câu trả lời khi có thời gian, nhưng không cần bây giờ. Vấn đề là bạn gần như không bao giờ cho phép bao gồm cả bắt đầu và kết thúc đồng thời. Trong ngành công nghiệp của tôi, thông thường coi sự khởi đầu là bao quát và kết thúc là bao gồm, nhưng một trong hai cách là tốt miễn là bạn kiên định. Đây là câu trả lời hoàn toàn chính xác đầu tiên cho câu hỏi này cho đến nay ... IMO.
Brian Gideon

14

Đây là một giải pháp khác sử dụng JavaScript. Đặc sản của giải pháp của tôi:

  • Xử lý các giá trị null là vô cùng
  • Giả sử rằng giới hạn dưới là bao gồm và giới hạn trên độc quyền.
  • Đi kèm với một loạt các bài kiểm tra

Các thử nghiệm dựa trên số nguyên nhưng vì các đối tượng ngày trong JavaScript có thể so sánh được nên bạn cũng có thể ném vào hai đối tượng ngày. Hoặc bạn có thể ném vào dấu thời gian mili giây.

Mã số:

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

Các xét nghiệm:

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

Kết quả khi chạy với karma & jasmine & PhantomJS:

PhantomJS 1.9.8 (Linux): Đã thực hiện 20 trong số 20 THÀNH CÔNG (0,003 giây / 0,004 giây)


9

tôi sẽ làm

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

Một IsBetweencái gì đó như thế nào

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

Tôi thích (trái <value && value <phải) || (phải <value && value <left) cho phương thức này.
Patrick Huizinga

Cám ơn vì cái này. Làm cho mọi thứ dễ dàng hơn trong đầu tôi.
sshow

1
Tại sao bạn phải kiểm tra bốn điều kiện khi bạn chỉ phải kiểm tra hai điều kiện? Thất bại.
ErikE

3
À, tôi xin lỗi, tôi thấy bây giờ bạn đang cho phép các phạm vi theo thứ tự ngược lại (StartDateX> EndDateX). Lạ thật. Dù sao, nếu StartDate1 nhỏ hơn StartDate2 và EndDate1 lớn hơn EndDate2 thì sao? Mã bạn đã cung cấp sẽ không phát hiện tình trạng chồng chéo này.
ErikE

3
Điều này sẽ không trả về sai nếu Date1 chứa toàn bộ Date2? Sau đó StartDate1 là trước StartDate2 và EndDate1 là sau EndDate2
user158037

9

nhập mô tả hình ảnh ở đây

Đây là đoạn mã làm nên điều kỳ diệu:

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

Ở đâu..

  • A -> 1 Bắt đầu
  • B -> 1E
  • C -> 2 Bắt đầu
  • D -> 2E

Bằng chứng? Kiểm tra ý chính mã bàn điều khiển thử nghiệm này .


Điều đó hoạt động, nhưng tôi muốn thử nghiệm để không chồng chéo, chỉ có hai kịch bản
John Albert

Cảm ơn đã giải thích điều này bằng cách sử dụng hình ảnh. Câu trả lời của bạn là giải pháp hoàn hảo cho câu hỏi này.
Rakesh Verma

8

Đây là giải pháp của tôi trong Java , cũng hoạt động trên các khoảng không giới hạn

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

Tôi nghĩ rằng bạn có nghĩa là kết thúc không giới hạn thay vì khoảng thời gian mở.
Henrik

@Henrik cả hai thuật ngữ đều hoạt động en.wikipedia.org/wiki/Interval_(mathatures)#Terminology
Khaled.K

!startA.after(endB)có nghĩa là startA <= endB và !endA.before(startB)có nghĩa là startB <= endA. Đây là các tiêu chí cho một khoảng thời gian đóng và không phải là một khoảng thời gian mở.
Henrik

@Henrik đúng và các điều kiện khác như endB == nullstartA == nullkiểm tra khoảng thời gian mở.
Khaled.K

1
endB == null, startA == null, endA == nullstartB == nullđều là những tiêu chí để kiểm tra một khoảng thời gian không giới hạn và không phải là một khoảng thời gian mở. Ví dụ cho sự khác biệt giữa các khoảng không giới hạn và khoảng mở: (10, 20) và (20, null) là hai khoảng mở không trùng nhau. Cái cuối cùng có một kết thúc không giới hạn. Hàm của bạn sẽ trả về true, nhưng các khoảng không trùng nhau, bởi vì các khoảng không bao gồm 20. (số được sử dụng thay vì dấu thời gian để đơn giản)
Henrik

7

Giải pháp được đăng ở đây không hoạt động cho tất cả các phạm vi chồng chéo ...

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

giải pháp làm việc của tôi là:

VÀ (
  ('start_date' GIỮA NGÔI SAO VÀ KẾT THÚC) - phục vụ cho bên ngoài và ngày kết thúc bên ngoài
  HOẶC LÀ
  ('end_date' GIỮA NGÔI SAO VÀ KẾT THÚC) - phục vụ cho bên ngoài và ngày bắt đầu bên ngoài
  HOẶC LÀ
  (NGÔI SAO GIỮA 'start_date' VÀ 'end_date') - chỉ cần một thứ duy nhất cho phạm vi bên ngoài có ngày ở bên trong.
) 

5

Đây là giải pháp javascript của tôi với khoảnh khắc.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;

4

Một cách dễ dàng để ghi nhớ giải pháp sẽ là
min(ends)>max(starts)


3

Trong Microsoft SQL SERVER - Hàm SQL

CREATE FUNCTION IsOverlapDates 
(
    @startDate1 as datetime,
    @endDate1 as datetime,
    @startDate2 as datetime,
    @endDate2 as datetime
)
RETURNS int
AS
BEGIN
DECLARE @Overlap as int
SET @Overlap = (SELECT CASE WHEN  (
        (@startDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and end date outer
        OR
        (@endDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and start date outer
        OR
        (@startDate2 BETWEEN @startDate1 AND @endDate1) -- only one needed for outer range where dates are inside.
        ) THEN 1 ELSE 0 END
    )
    RETURN @Overlap

END
GO

--Execution of the above code
DECLARE @startDate1 as datetime
DECLARE @endDate1 as datetime
DECLARE @startDate2 as datetime
DECLARE @endDate2 as datetime
DECLARE @Overlap as int
SET @startDate1 = '2014-06-01 01:00:00' 
SET @endDate1 =   '2014-06-01 02:00:00'
SET @startDate2 = '2014-06-01 01:00:00' 
SET @endDate2 =   '2014-06-01 01:30:00'

SET @Overlap = [dbo].[IsOverlapDates]  (@startDate1, @endDate1, @startDate2, @endDate2)

SELECT Overlap = @Overlap

3

điều đơn giản nhất

Cách đơn giản nhất là sử dụng một thư viện chuyên dụng được thiết kế tốt cho công việc thời gian.

someInterval.overlaps( anotherInterval )

java.time & ThreeTen-Extra

Điều tốt nhất trong kinh doanh là java.timekhung được xây dựng trong Java 8 trở lên. Thêm vào đó là dự án ThreeTen-Extra bổ sung java.time với các lớp bổ sung, cụ thể là Intervallớp chúng ta cần ở đây.

Đối với language-agnosticthẻ trong Câu hỏi này, mã nguồn cho cả hai dự án có sẵn để sử dụng trong các ngôn ngữ khác (lưu ý giấy phép của họ).

Interval

Các org.threeten.extra.Intervallớp học rất thuận tiện, nhưng đòi hỏi ngày thời gian khoảnh khắc ( java.time.Instantđối tượng) chứ không phải là giá trị ngày-only. Vì vậy, chúng tôi tiến hành bằng cách sử dụng thời điểm đầu tiên trong ngày trong UTC để thể hiện ngày.

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

Tạo một Intervalđại diện cho khoảng thời gian đó.

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

Chúng ta cũng có thể định nghĩa một Intervalvới một thời điểm bắt đầu cộng với a Duration.

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

So sánh để kiểm tra sự chồng chéo là dễ dàng.

Boolean overlaps = interval_A.overlaps( interval_B );

Bạn có thể so sánh Intervalvới một Intervalhoặc khác Instant:

Tất cả những điều này sử dụng Half-Opencách tiếp cận để xác định một khoảng thời gian trong đó bắt đầu là bao gồm và kết thúc là độc quyền .


3

Đây là phần mở rộng cho câu trả lời xuất sắc của @ charles-bretana.

Tuy nhiên, câu trả lời không tạo ra sự khác biệt giữa các khoảng thời gian mở, đóng và nửa mở (hoặc nửa đóng).

Trường hợp 1 : A, B là các khoảng đóng

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

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


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

Chồng chéo iff: (StartA <= EndB) and (EndA >= StartB)

Trường hợp 2 : A, B là các khoảng mở

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

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

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

Chồng chéo iff: (StartA < EndB) and (EndA > StartB)

Trường hợp 3 : A, B phải mở

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

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

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

Điều kiện chồng chéo: (StartA < EndB) and (EndA > StartB)

Trường hợp 4 : A, B bỏ ngỏ

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

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

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

Điều kiện chồng chéo: (StartA < EndB) and (EndA > StartB)

Trường hợp 5 : A mở, B đóng

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

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


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

Điều kiện chồng chéo: (StartA <= EndB) and (EndA > StartB)

Vân vân...

Cuối cùng, điều kiện chung cho hai khoảng thời gian trùng nhau là

(StartA <🞐 EndB) và (EndA> StartB)

trong đó biến bất đẳng thức nghiêm ngặt thành bất đẳng thức bất cứ khi nào việc so sánh được thực hiện giữa hai điểm cuối bao gồm.


Các trường hợp hai, ba và bốn có cùng điều kiện chồng chéo, đây có phải là cố ý không?
Marie

@Marie, tôi chỉ liệt kê một vài trường hợp (không phải tất cả)
user2314737

Điều này, nhưng được xây dựng như câu trả lời của Jonathan Leffler sẽ là những gì tôi nghĩ trong đầu như là câu trả lời được chấp nhận cho câu hỏi của OP.
mbx

3

Câu trả lời ngắn bằng khoảnh khắc :

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

câu trả lời dựa trên các câu trả lời ở trên, nhưng nó được rút ngắn.


2

Trong trường hợp bạn đang sử dụng phạm vi ngày chưa kết thúc (vẫn đang tiếp tục), ví dụ: không đặt endDate = '0000-00-00', bạn không thể sử dụng GIỮA vì 0000-00-00 không phải là ngày hợp lệ!

Tôi đã sử dụng giải pháp này:

(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

Nếu startdate2 cao hơn thì enddate không có sự trùng lặp!


2

Câu trả lời quá đơn giản đối với tôi vì vậy tôi đã tạo một câu lệnh SQL động chung chung hơn để kiểm tra xem một người có bất kỳ ngày chồng chéo nào không.

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

Giải pháp toán học được cung cấp bởi @Bretana là tốt nhưng bỏ qua hai chi tiết cụ thể:

  1. khía cạnh của khoảng thời gian đóng hoặc nửa mở
  2. khoảng trống

Về trạng thái đóng hoặc mở của các ranh giới khoảng, giải pháp của @Bretana hợp lệ cho các khoảng thời gian đóng

(StartA <= EndB) và (EndA> = StartB)

có thể được viết lại trong khoảng thời gian nửa mở để:

(StartA <EndB) và (EndA> StartB)

Việc hiệu chỉnh này là cần thiết bởi vì một ranh giới khoảng mở không thuộc phạm vi giá trị của một khoảng theo định nghĩa.


Và về các khoảng trống , tốt, ở đây mối quan hệ hiển thị ở trên KHÔNG giữ. Các khoảng trống không chứa bất kỳ giá trị hợp lệ theo định nghĩa phải được xử lý như trường hợp đặc biệt. Tôi chứng minh điều đó bằng thư viện thời gian Java Time4J của tôi thông qua ví dụ này:

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)

Dấu ngoặc vuông hàng đầu "[" biểu thị bắt đầu đóng trong khi dấu ngoặc cuối ")" biểu thị kết thúc mở.

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

Như đã trình bày ở trên, các khoảng trống vi phạm điều kiện chồng lấp ở trên (đặc biệt là startA <endB), do đó Time4J (và các thư viện khác) cũng phải xử lý nó như trường hợp cạnh đặc biệt để đảm bảo rằng sự chồng chéo của bất kỳ khoảng tùy ý nào với một khoảng trống không tồn tại. Tất nhiên, các khoảng thời gian (được đóng theo mặc định trong Time4J nhưng cũng có thể được mở một nửa, giống như các khoảng ngày trống) được xử lý theo cách tương tự.


1

Đây là một phương pháp chung có thể hữu ích tại địa phương.

    // 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
Tâm để thêm một số từ giải thích?
Phantômaxx

1

Sử dụng Java produc.Date, đây là những gì tôi đã là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

Theo tôi, cách dễ nhất để làm điều đó là so sánh nếu EndDate1 trước StartDate2 và EndDate2 trước StartDate1.

Điều đó tất nhiên nếu bạn đang xem xét các khoảng thời gian mà StartDate luôn ở trước EndDate.


1

Tôi đã có một tình huống mà chúng tôi đã có ngày thay vì datetimes và ngày chỉ có thể trùng nhau khi bắt đầu / kết thúc. Ví dụ dưới đây:

nhập mô tả hình ảnh ở đây

(Màu xanh lá cây là khoảng thời gian hiện tại, khối màu xanh là khoảng thời gian hợp lệ, màu đỏ là khoảng thời gian chồng chéo).

Tôi đã điều chỉnh câu trả lời của Ian Nelson cho giải pháp sau:

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

Điều này phù hợp với tất cả các trường hợp chồng lấp nhưng bỏ qua những trường hợp chồng chéo được phép.


0

Chia vấn đề thành các trường hợp sau đó xử lý từng trường hợp .

Tình huống 'hai phạm vi ngày giao nhau' được bao phủ bởi hai trường hợp - phạm vi ngày đầu tiên bắt đầu trong phạm vi thứ hai hoặc phạm vi ngày thứ hai bắt đầu trong phạm vi ngày đầu tiên.


0

Bạn có thể thử điều này:

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

Đây là giải pháp của tôi, nó trả về đúng khi các giá trị không trùng nhau:

X BẮT ĐẦU 1 Y KẾT 1

A BẮT ĐẦU 2 B KẾT 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

Đối với ruby ​​tôi cũng tìm thấy điều này:

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

Tìm thấy nó ở đây với lời giải thích hay -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails


0

Truy vấn bên dưới cung cấp cho tôi các id mà phạm vi ngày được cung cấp (ngày bắt đầu và ngày kết thúc trùng với bất kỳ ngày nào (ngày bắt đầu và ngày kết thúc) trong tên_bảng của tôi

select id from table_name where (START_DT_TM >= 'END_DATE_TIME'  OR   
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.