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
và StartDate2
đến EndDate2
.
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
và StartDate2
đến EndDate2
.
Câu trả lời:
(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 > endA
hoặ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()
và 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)
Start
và End
ý 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.
start
và end
(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)
DateRangesOverlap = max(start1, start2) < min(end1, end2)
Tôi tin rằng đủ để nói rằng hai phạm vi trùng nhau nếu:
(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)
(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.
<=
thành <
nếu bắt đầu là bao gồm và kết thúc là độc quyền.
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
Để 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 A
và B
với các thành phần .start
và .end
rà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 >
và <=
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-------|----------------------
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) {
...
}
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.
Đâ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:
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.
/**
* Compares to comparable objects to find out whether they overlap.
* It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive).
* A null value is interpreted as infinity
*/
function intervalsOverlap(from1, to1, from2, to2) {
return (to2 === null || from1 < to2) && (to1 === null || to1 > from2);
}
describe('', function() {
function generateTest(firstRange, secondRange, expected) {
it(JSON.stringify(firstRange) + ' and ' + JSON.stringify(secondRange), function() {
expect(intervalsOverlap(firstRange[0], firstRange[1], secondRange[0], secondRange[1])).toBe(expected);
});
}
describe('no overlap (touching ends)', function() {
generateTest([10,20], [20,30], false);
generateTest([20,30], [10,20], false);
generateTest([10,20], [20,null], false);
generateTest([20,null], [10,20], false);
generateTest([null,20], [20,30], false);
generateTest([20,30], [null,20], false);
});
describe('do overlap (one end overlaps)', function() {
generateTest([10,20], [19,30], true);
generateTest([19,30], [10,20], true);
generateTest([10,20], [null,30], true);
generateTest([10,20], [19,null], true);
generateTest([null,30], [10,20], true);
generateTest([19,null], [10,20], true);
});
describe('do overlap (one range included in other range)', function() {
generateTest([10,40], [20,30], true);
generateTest([20,30], [10,40], true);
generateTest([10,40], [null,null], true);
generateTest([null,null], [10,40], true);
});
describe('do overlap (both ranges equal)', function() {
generateTest([10,20], [10,20], true);
generateTest([null,20], [null,20], true);
generateTest([10,null], [10,null], true);
generateTest([null,null], [null,null], true);
});
});
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)
tôi sẽ làm
StartDate1.IsBetween(StartDate2, EndDate2) || EndDate1.IsBetween(StartDate2, EndDate2)
Một IsBetween
cá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);
}
Đâ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..
Bằng chứng? Kiểm tra ý chính mã bàn điều khiển thử nghiệm này .
Đâ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));
}
!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ở.
endB == null
và startA == null
kiểm tra khoảng thời gian mở.
endB == null
, startA == null
, endA == null
Và startB == 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)
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. )
Đâ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;
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
đ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 )
Điều tốt nhất trong kinh doanh là java.time
khung đượ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à Interval
lớp chúng ta cần ở đây.
Đối với language-agnostic
thẻ 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.Interval
lớ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 Interval
vớ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 Interval
với một Interval
hoặc khác Instant
:
Tất cả những điều này sử dụng Half-Open
cá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 .
Đâ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â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.
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!
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)
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ể:
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ự.
Đâ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
*/
}
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");
}
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;
}
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.
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:
(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.
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.
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);
Đâ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
Đố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
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'))