เมื่อกำหนดช่วงวันที่สองช่วงวิธีที่ง่ายที่สุดหรือมีประสิทธิภาพมากที่สุดในการพิจารณาว่าช่วงวันที่สองช่วงนี้ทับซ้อนกันกันอย่างไร
ตัวอย่างเช่นสมมติว่าเรามีช่วงแสดงโดยตัวแปร DateTime StartDate1
ไปEndDate1
และ จะStartDate2
EndDate2
เมื่อกำหนดช่วงวันที่สองช่วงวิธีที่ง่ายที่สุดหรือมีประสิทธิภาพมากที่สุดในการพิจารณาว่าช่วงวันที่สองช่วงนี้ทับซ้อนกันกันอย่างไร
ตัวอย่างเช่นสมมติว่าเรามีช่วงแสดงโดยตัวแปร DateTime StartDate1
ไปEndDate1
และ จะStartDate2
EndDate2
คำตอบ:
(StartA <= EndB) และ (EndA> = StartB)
หลักฐาน:
ให้ ConditionA หมายความว่า DateRange A สมบูรณ์หลังจาก DateRange B
_ |---- DateRange A ------|
|---Date Range B -----| _
(จริงถ้าStartA > EndB
)
ให้ ConditionB หมายความว่า DateRange A นั้นสมบูรณ์ก่อน DateRange B
|---- DateRange A -----| _
_ |---Date Range B ----|
(จริงถ้าEndA < StartB
)
จากนั้นจึงมีการทับซ้อนกันถ้าทั้ง A Nor B เป็นจริง -
(หากช่วงหนึ่งไม่สมบูรณ์หลังจากช่วงอื่น ๆ
หรือไม่สมบูรณ์ก่อนอีกช่วงหนึ่งพวกเขาจะต้องทับซ้อนกัน)
ตอนนี้หนึ่งในกฎของDe Morganกล่าวว่า:
Not (A Or B)
<=> Not A And Not B
ซึ่งแปลเป็น: (StartA <= EndB) and (EndA >= StartB)
หมายเหตุ: รวมถึงเงื่อนไขที่ขอบซ้อนกันทุกประการ หากคุณต้องการยกเว้นสิ่งเหล่านี้ให้
เปลี่ยน>=
โอเปอเรเตอร์เป็น>
และ<=
เพื่อ<
โน้ต 2. ขอขอบคุณที่ @Baodad ดูบล็อกนี้ที่ทับซ้อนที่เกิดขึ้นจริงเป็นอย่างน้อย:
{ endA-startA
, endA - startB
, endB-startA
, endB - startB
}
(StartA <= EndB) and (EndA >= StartB)
(StartA <= EndB) and (StartB <= EndA)
Note3 ขอบคุณ @tomosius รุ่นที่สั้นกว่าอ่าน:
DateRangesOverlap = max(start1, start2) < min(end1, end2)
นี่เป็นช็อตคัทการสร้างประโยคสำหรับการใช้งานที่ยาวนานขึ้นซึ่งรวมถึงการตรวจสอบพิเศษเพื่อตรวจสอบว่าวันที่เริ่มต้นเป็นวันที่หรือก่อนสิ้นสุดวันที่ สืบทอดสิ่งนี้จากด้านบน:
หากเริ่มต้นและสิ้นสุดวันที่สามารถออกคำสั่งเช่นถ้าเป็นไปได้ว่าstartA > endA
หรือstartB > endB
แล้วคุณยังมีการตรวจสอบว่าพวกเขาอยู่ในการสั่งซื้อเพื่อให้หมายความว่าคุณต้องเพิ่มสองกฎความถูกต้องเพิ่มเติม:
(StartA <= EndB) and (StartB <= EndA) and (StartA <= EndA) and (StartB <= EndB)
หรือ:
(StartA <= EndB) and (StartA <= EndA) and (StartB <= EndA) and (StartB <= EndB)
หรือ
(StartA <= Min(EndA, EndB) and (StartB <= Min(EndA, EndB))
หรือ:
(Max(StartA, StartB) <= Min(EndA, EndB)
แต่ในการติดตั้งMin()
และMax()
คุณต้องใช้โค้ด (โดยใช้ภาษาซีประกอบไปด้วยความอดทน):
(StartA > StartB? Start A: StartB) <= (EndA < EndB? EndA: EndB)
Start
และความEnd
หมาย หากคุณมีสองตัวแปรชื่อด้านบนและด้านล่างหรือตะวันออกและตะวันตกหรือ HighValue และ LoValue ก็สามารถสันนิษฐานหรือบอกเป็นนัยว่าบางสิ่งบางอย่างหรือบางคนควรจะมั่นใจว่าหนึ่งในคู่ของค่าจะไม่ถูกเก็บไว้ในตัวแปรตรงกันข้าม - เพียงหนึ่งในสองคู่เท่านั้นเพราะมันจะทำงานหากค่าทั้งคู่เปลี่ยนไป
start
และend
(ด้วยความหมายว่า "null start" = "จากจุดเริ่มต้นของเวลา" และ "null end" = "ถึงจุดสิ้นสุดของเวลา") เช่นนี้:(startA === null || endB === null || startA <= endB) && (endA === null || startB === null || endA >= startB)
DateRangesOverlap = max(start1, start2) < min(end1, end2)
ฉันเชื่อว่ามันเพียงพอที่จะบอกว่าทั้งสองช่วงทับกันถ้า:
(StartDate1 <= EndDate2) and (StartDate2 <= EndDate1)
(StartDate1 <= EndDate2) and (EndDate1 >= StartDate2)
สัญกรณ์ง่ายต่อการเข้าใจ Range1 อยู่ด้านซ้ายเสมอในการทดสอบ
<=
เป็น<
ถ้าเริ่มต้นรวมและสิ้นสุดเป็นเอกสิทธิ์
บทความนี้ช่วงเวลาห้องสมุดสำหรับ. NETอธิบายความสัมพันธ์ของสองช่วงเวลาโดยการแจงนับPeriodRelation :
// ------------------------------------------------------------------------
public enum PeriodRelation
{
After,
StartTouching,
StartInside,
InsideStartTouching,
EnclosingStartTouching,
Enclosing,
EnclosingEndTouching,
ExactMatch,
Inside,
InsideEndTouching,
EndInside,
EndTouching,
Before,
} // enum PeriodRelation
สำหรับเหตุผลเกี่ยวกับความสัมพันธ์ชั่วคราว (หรือความสัมพันธ์ระหว่างช่วงเวลาอื่น ๆ มานั้น) พิจารณาอัลเลนช่วงพีชคณิต มันอธิบายถึง 13 ความสัมพันธ์ที่เป็นไปได้ที่สองช่วงเวลาสามารถมีความเคารพซึ่งกันและกัน คุณสามารถค้นหาข้อมูลอ้างอิงอื่น ๆ - "Allen Interval" ดูเหมือนจะเป็นคำที่ใช้ในการค้นหา คุณยังสามารถค้นหาข้อมูลเกี่ยวกับการดำเนินการเหล่านี้ได้ในแอพพลิเคชั่นการพัฒนาเชิงเวลาของ Snodgrass ใน SQL (PDF มีให้ทางออนไลน์ที่ URL) และในวันที่ Darwen และ Lorentzos Temporal Data และ Relational Model (2002) หรือ เวลาและทฤษฎีเชิงสัมพันธ์: ฐานข้อมูลชั่วคราว Relational Model และ SQL (2014; รุ่นที่สองของ TD&RM อย่างมีประสิทธิภาพ)
คำตอบสั้น ๆ (ish) คือ: กำหนดช่วงเวลาสองวันA
และB
ด้วยองค์ประกอบ.start
และ.end
และข้อ จำกัด.start <= .end
จากนั้นช่วงเวลาสองช่วงจะทับซ้อนกันถ้า:
A.end >= B.start AND A.start <= B.end
คุณสามารถปรับใช้>=
vs >
และ<=
vs <
เพื่อตอบสนองความต้องการของคุณสำหรับระดับของการทับซ้อน
ความคิดเห็น ErikE:
คุณจะได้ 13 ถ้าคุณนับเรื่องตลก ๆ ... ฉันจะได้ "15 ความสัมพันธ์ที่เป็นไปได้ที่ช่วงเวลาสองช่วงสามารถมีได้" เมื่อฉันคลั่งไคล้มัน ด้วยการนับที่สมเหตุสมผลฉันได้เพียงหกและถ้าคุณละทิ้งความสนใจว่า A หรือ B มาก่อนฉันจะได้แค่สาม (ไม่มีจุดตัดแยกบางส่วนหนึ่งในทั้งหมด) 15 ไปเช่นนี้: [ก่อนหน้า: ก่อน, เริ่ม, ภายใน, จบ, หลัง], [เริ่มต้น: เริ่ม, ภายใน, จบ, หลัง], [ภายใน: ภายใน, ปลาย, หลัง], [ปลาย: ปลาย, หลัง], [ หลังจากที่: หลังจาก]
ฉันคิดว่าคุณไม่สามารถนับทั้งสองรายการ 'ก่อนหน้า: ก่อนหน้า' และ 'หลัง: หลังจาก' ฉันเห็นรายการ 7 รายการถ้าคุณเทียบความสัมพันธ์กับผู้รุกราน (ดูแผนภาพใน Wikipedia URL ที่อ้างอิงมันมี 7 รายการซึ่ง 6 รายการนั้นมีค่าผกผันที่แตกต่างกัน และสามคนนั้นมีเหตุผลหรือไม่ขึ้นอยู่กับความต้องการของคุณ
----------------------|-------A-------|----------------------
|----B1----|
|----B2----|
|----B3----|
|----------B4----------|
|----------------B5----------------|
|----B6----|
----------------------|-------A-------|----------------------
|------B7-------|
|----------B8-----------|
|----B9----|
|----B10-----|
|--------B11--------|
|----B12----|
|----B13----|
----------------------|-------A-------|----------------------
หากควรคำนวณการทับซ้อนกันด้วยคุณสามารถใช้สูตรต่อไปนี้:
overlap = max(0, min(EndDate1, EndDate2) - max(StartDate1, StartDate2))
if (overlap > 0) {
...
}
วิธีแก้ปัญหาทั้งหมดที่ตรวจสอบเงื่อนไขจำนวนมากขึ้นอยู่กับว่าช่วงใดสัมพันธ์กับอีกช่วงหนึ่งสามารถทำให้ง่ายขึ้นอย่างมากเพียงแค่ตรวจสอบให้แน่ใจว่าช่วงเฉพาะเริ่มต้นขึ้นก่อน! คุณมั่นใจได้ว่าช่วงแรกเริ่มต้นก่อนหน้า (หรือในเวลาเดียวกัน) โดยการสลับช่วงหากจำเป็นขึ้นหน้า
จากนั้นคุณสามารถตรวจจับการทับซ้อนกันได้ถ้าช่วงเริ่มต้นอื่นน้อยกว่าหรือเท่ากับช่วงสิ้นสุดช่วงแรก (ถ้าช่วงนั้นรวมอยู่ด้วยซึ่งมีทั้งเวลาเริ่มต้นและสิ้นสุด) หรือน้อยกว่า (ถ้าช่วงนั้นรวมช่วงเริ่มต้นและไม่รวมช่วงสิ้นสุด) .
สมมติว่ามีทั้งสองด้านมีความเป็นไปได้เพียงสี่อย่างเท่านั้นซึ่งความเป็นไปได้ที่ไม่ทับซ้อนกัน:
|----------------------| range 1
|---> range 2 overlap
|---> range 2 overlap
|---> range 2 overlap
|---> range 2 no overlap
จุดสิ้นสุดของช่วง 2 ไม่ได้ใส่เข้าไป ดังนั้นในรหัสหลอก:
def doesOverlap (r1, r2):
if r1.s > r2.s:
swap r1, r2
if r2.s > r1.e:
return false
return true
สิ่งนี้สามารถทำให้เข้าใจง่ายยิ่งขึ้นไปอีก:
def doesOverlap (r1, r2):
if r1.s > r2.s:
swap r1, r2
return r2.s <= r1.e
หากช่วงนั้นรวมอยู่ที่จุดเริ่มต้นและแบบเอกสิทธิ์ในตอนท้ายคุณเพียงแค่ต้องแทนที่>
ด้วย>=
ในif
คำสั่งที่สอง(สำหรับส่วนรหัสแรก: ในส่วนรหัสที่สองคุณจะใช้<
แทน<=
):
|----------------------| range 1
|---> range 2 overlap
|---> range 2 overlap
|---> range 2 no overlap
|---> range 2 no overlap
คุณ จำกัด จำนวนของการตรวจสอบที่คุณต้องทำอย่างมากเนื่องจากคุณลบครึ่งหนึ่งของพื้นที่ปัญหาก่อนโดยตรวจสอบให้แน่ใจว่าช่วงที่ 1 ไม่เริ่มต้นหลังจากช่วงที่ 2
นี่เป็นอีกวิธีการใช้ JavaScript ความเชี่ยวชาญพิเศษของโซลูชันของฉัน:
การทดสอบจะขึ้นอยู่กับจำนวนเต็ม แต่เนื่องจากวัตถุวันที่ใน JavaScript นั้นเทียบเคียงได้คุณก็สามารถโยนวัตถุวันที่สองรายการได้เช่นกัน หรือคุณสามารถโยนในเวลาประทับมิลลิวินาที
/**
* 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);
});
});
ผลลัพธ์เมื่อทำงานด้วยกรรม & จัสมิน & PhantomJS:
PhantomJS 1.9.8 (Linux): ดำเนินการ 20 จาก 20 สำเร็จ (0.003 วินาที / 0.004 วินาที)
ฉันจะทำ
StartDate1.IsBetween(StartDate2, EndDate2) || EndDate1.IsBetween(StartDate2, EndDate2)
IsBetween
บางสิ่งอยู่ที่ไหน
public static bool IsBetween(this DateTime value, DateTime left, DateTime right) {
return (value > left && value < right) || (value < left && value > right);
}
นี่คือรหัสที่ใช้เวทมนตร์:
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));
อยู่ที่ไหน ..
นี่คือทางออกของฉันในJavaซึ่งทำงานในช่วงเวลาไม่ จำกัด
private Boolean overlap (Timestamp startA, Timestamp endA,
Timestamp startB, Timestamp endB)
{
return (endB == null || startA == null || !startA.after(endB))
&& (endA == null || startB == null || !endA.before(startB));
}
!startA.after(endB)
หมายถึง startA <= endB และ!endA.before(startB)
หมายถึง startB <= endA สิ่งเหล่านี้เป็นเกณฑ์สำหรับช่วงปิดและไม่ใช่ช่วงเวลาเปิด
endB == null
และstartA == null
ตรวจสอบช่วงเวลาที่เปิด
endB == null
, startA == null
, endA == null
และstartB == null
มีเกณฑ์ทั้งหมดเพื่อตรวจสอบช่วงเวลามากมายและไม่ได้เป็นช่วงเวลาที่เปิดให้บริการ ตัวอย่างสำหรับความแตกต่างระหว่างช่วงเวลาที่ไม่ จำกัด และเปิด: (10, 20) และ (20, null) เป็นช่วงเวลาเปิดสองช่วงที่ไม่ทับซ้อนกัน อันสุดท้ายมีจุดจบไม่ จำกัด ฟังก์ชั่นของคุณจะกลับมาจริง แต่ช่วงเวลาไม่ทับซ้อนกันเพราะช่วงเวลาไม่รวม 20 (ตัวเลขที่ใช้แทนการประทับเวลาเพื่อความง่าย)
โซลูชันที่โพสต์ที่นี่ไม่ทำงานสำหรับช่วงที่ทับซ้อนกันทั้งหมด ...
---------------------- | ------- ------- | ----------- ----------- | ---- B1 ---- | | ---- B2 ---- | | ---- B3 ---- | | ---------- B4 ---------- | | ---------------- B5 ---------------- | | ---- B6 ---- | ---------------------- | ------- ------- | ----------- ----------- | ------ B7 ------- | | ---------- B8 ----------- | | ---- B9 ---- | | ---- B10 ----- | | -------- B11 -------- | | ---- B12 ---- | | ---- B13 ---- | ---------------------- | ------- ------- | ----------- -----------
วิธีการทำงานของฉันคือ:
และ ( ('start_date' ระหว่างวันที่เริ่มต้นและวันที่สิ้นสุด) - เหมาะสำหรับวันที่ด้านในและด้านนอกวันที่ หรือ ('end_date' ระหว่างวันที่เริ่มต้นและวันที่สิ้นสุด) - เหมาะสำหรับวันที่ด้านนอกและวันที่เริ่มต้น หรือ (STARTDATE ระหว่าง 'start_date' และ 'end_date') - เพียงอันเดียวที่จำเป็นสำหรับช่วงนอกที่มีวันที่อยู่ภายใน )
นี่คือโซลูชันจาวาสคริปต์ของฉันกับ 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;
วิธีง่ายๆในการจดจำวิธีแก้ปัญหาก็คือ
min(ends)>max(starts)
ใน Microsoft SQL SERVER - ฟังก์ชัน SQL
CREATE FUNCTION IsOverlapDates
(
@startDate1 as datetime,
@endDate1 as datetime,
@startDate2 as datetime,
@endDate2 as datetime
)
RETURNS int
AS
BEGIN
DECLARE @Overlap as int
SET @Overlap = (SELECT CASE WHEN (
(@startDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and end date outer
OR
(@endDate1 BETWEEN @startDate2 AND @endDate2) -- caters for inner and start date outer
OR
(@startDate2 BETWEEN @startDate1 AND @endDate1) -- only one needed for outer range where dates are inside.
) THEN 1 ELSE 0 END
)
RETURN @Overlap
END
GO
--Execution of the above code
DECLARE @startDate1 as datetime
DECLARE @endDate1 as datetime
DECLARE @startDate2 as datetime
DECLARE @endDate2 as datetime
DECLARE @Overlap as int
SET @startDate1 = '2014-06-01 01:00:00'
SET @endDate1 = '2014-06-01 02:00:00'
SET @startDate2 = '2014-06-01 01:00:00'
SET @endDate2 = '2014-06-01 01:30:00'
SET @Overlap = [dbo].[IsOverlapDates] (@startDate1, @endDate1, @startDate2, @endDate2)
SELECT Overlap = @Overlap
ง่ายที่สุด
วิธีที่ง่ายที่สุดคือการใช้ไลบรารี่ที่ได้รับการออกแบบมาเป็นอย่างดีสำหรับงานวันที่
someInterval.overlaps( anotherInterval )
สิ่งที่ดีที่สุดในธุรกิจคือjava.time
กรอบการทำงานที่สร้างขึ้นใน Java 8 และใหม่กว่า เพิ่มไปยังโครงการThreeTen-Extraที่เสริม java.time ด้วยคลาสเพิ่มเติมโดยเฉพาะInterval
คลาสที่เราต้องการที่นี่
สำหรับlanguage-agnostic
แท็กในคำถามนี้ซอร์สโค้ดสำหรับทั้งสองโครงการนั้นมีให้ใช้ในภาษาอื่น (คำนึงถึงสิทธิ์ใช้งาน)
Interval
org.threeten.extra.Interval
ระดับเป็นประโยชน์ แต่ต้องใช้ช่วงเวลาวันที่เวลา ( java.time.Instant
วัตถุ) มากกว่าค่าวันเท่านั้น ดังนั้นเราจึงดำเนินการโดยใช้ช่วงเวลาแรกของวันใน UTC เพื่อแสดงวันที่
Instant start = Instant.parse( "2016-01-01T00:00:00Z" );
Instant stop = Instant.parse( "2016-02-01T00:00:00Z" );
สร้างInterval
เพื่อเป็นตัวแทนช่วงเวลานั้น
Interval interval_A = Interval.of( start , stop );
นอกจากนี้เรายังสามารถกำหนดกับช่วงเวลาที่เริ่มต้นบวกInterval
Duration
Instant start_B = Instant.parse( "2016-01-03T00:00:00Z" );
Interval interval_B = Interval.of( start_B , Duration.of( 3 , ChronoUnit.DAYS ) );
การเปรียบเทียบเพื่อทดสอบการทับซ้อนนั้นง่าย
Boolean overlaps = interval_A.overlaps( interval_B );
คุณสามารถเปรียบเทียบการเปรียบเทียบInterval
กับรายการอื่นInterval
หรือInstant
:
ทั้งหมดเหล่านี้ใช้Half-Open
วิธีการที่จะกำหนดช่วงเวลาที่จุดเริ่มต้นคือการรวมและตอนจบเป็นพิเศษ
นี่คือส่วนขยายของคำตอบที่ยอดเยี่ยมโดย @ charles-bretana
อย่างไรก็ตามคำตอบไม่ได้สร้างความแตกต่างระหว่างช่วงเปิดปิดและครึ่งเปิด (หรือปิดครึ่ง)
กรณีที่ 1 : A, B เป็นช่วงปิด
A = [StartA, EndA]
B = [StartB, EndB]
[---- DateRange A ------] (True if StartA > EndB)
[--- Date Range B -----]
[---- DateRange A -----] (True if EndA < StartB)
[--- Date Range B ----]
ซ้อนทับ iff: (StartA <= EndB) and (EndA >= StartB)
กรณีที่ 2 : A, B เป็นช่วงเวลาที่เปิด
A = (StartA, EndA)
B = (StartB, EndB)
(---- DateRange A ------) (True if StartA >= EndB)
(--- Date Range B -----)
(---- DateRange A -----) (True if EndA <= StartB)
(--- Date Range B ----)
ซ้อนทับ iff: (StartA < EndB) and (EndA > StartB)
กรณีที่ 3 : A, B เปิดทันที
A = [StartA, EndA)
B = [StartB, EndB)
[---- DateRange A ------) (True if StartA >= EndB)
[--- Date Range B -----)
[---- DateRange A -----) (True if EndA <= StartB)
[--- Date Range B ----)
สภาพที่ทับซ้อนกัน: (StartA < EndB) and (EndA > StartB)
กรณีที่ 4 : A, B เปิดทิ้งไว้
A = (StartA, EndA]
B = (StartB, EndB]
(---- DateRange A ------] (True if StartA >= EndB)
(--- Date Range B -----]
(---- DateRange A -----] (True if EndA <= StartB)
(--- Date Range B ----]
สภาพที่ทับซ้อนกัน: (StartA < EndB) and (EndA > StartB)
กรณีที่ 5 : เปิดขวา B ปิด
A = [StartA, EndA)
B = [StartB, EndB]
[---- DateRange A ------) (True if StartA > EndB)
[--- Date Range B -----]
[---- DateRange A -----) (True if EndA <= StartB)
[--- Date Range B ----]
สภาพที่ทับซ้อนกัน: (StartA <= EndB) and (EndA > StartB)
ฯลฯ ...
ในที่สุดสภาพทั่วไปสำหรับสองช่วงเวลาที่จะทับซ้อนกันคือ
(StartA <🞐 EndB) และ (EndA> 🞐 StartB)
โดยที่🞐เปลี่ยนความไม่เท่าเทียมอย่างเข้มงวดเป็นหนึ่งที่ไม่เข้มงวดเมื่อใดก็ตามที่มีการเปรียบเทียบระหว่างจุดรวมสองจุด
คำตอบสั้น ๆ ที่ใช้ momentjs :
function isOverlapping(startDate1, endDate1, startDate2, endDate2){
return moment(startDate1).isSameOrBefore(endDate2) &&
moment(startDate2).isSameOrBefore(endDate1);
}
คำตอบนั้นขึ้นอยู่กับคำตอบข้างต้น แต่มันสั้นลง
ในกรณีที่คุณใช้ช่วงวันที่ที่ยังไม่สิ้นสุด (ยังดำเนินต่อไป) เช่นไม่ได้ตั้งค่า endDate = '0000-00-00' คุณไม่สามารถใช้ BETWEEN ได้เนื่องจาก 0000-00-00 ไม่ใช่วันที่ที่ถูกต้อง!
ฉันใช้วิธีนี้:
(Startdate BETWEEN '".$startdate2."' AND '".$enddate2."') //overlap: starts between start2/end2
OR (Startdate < '".$startdate2."'
AND (enddate = '0000-00-00' OR enddate >= '".$startdate2."')
) //overlap: starts before start2 and enddate not set 0000-00-00 (still on going) or if enddate is set but higher then startdate2
หาก startdate2 สูงกว่า enddate จะไม่มีการทับซ้อน!
คำตอบนั้นง่ายเกินไปสำหรับฉันดังนั้นฉันจึงได้สร้างคำสั่ง SQL แบบไดนามิกทั่วไปซึ่งจะตรวจสอบว่าบุคคลนั้นมีวันที่ทับซ้อนกันหรือไม่
SELECT DISTINCT T1.EmpID
FROM Table1 T1
INNER JOIN Table2 T2 ON T1.EmpID = T2.EmpID
AND T1.JobID <> T2.JobID
AND (
(T1.DateFrom >= T2.DateFrom AND T1.dateFrom <= T2.DateTo)
OR (T1.DateTo >= T2.DateFrom AND T1.DateTo <= T2.DateTo)
OR (T1.DateFrom < T2.DateFrom AND T1.DateTo IS NULL)
)
AND NOT (T1.DateFrom = T2.DateFrom)
วิธีการแก้ปัญหาทางคณิตศาสตร์ที่ได้รับจาก @Bretana นั้นดี แต่ไม่สนใจสองรายละเอียดเฉพาะ
เกี่ยวกับสถานะปิดหรือสถานะเปิดของขอบเขตช่วงเวลาวิธีแก้ปัญหาของ @Bretana ใช้ได้สำหรับช่วงเวลาปิด
(StartA <= EndB) และ (EndA> = StartB)
สามารถเขียนใหม่สำหรับช่วงครึ่งเปิดเพื่อ:
(StartA <EndB) และ (EndA> StartB)
การแก้ไขนี้เป็นสิ่งจำเป็นเนื่องจากขอบเขตช่วงเวลาเปิดไม่ได้อยู่ในช่วงค่าของช่วงเวลาตามคำจำกัดความ
และเกี่ยวกับช่วงเวลาว่างเปล่าที่นี่ความสัมพันธ์ที่แสดงด้านบนไม่ถือ ช่วงเวลาว่างที่ไม่มีค่าที่ถูกต้องตามนิยามจะต้องถูกจัดการเป็นกรณีพิเศษ ฉันสาธิตโดยใช้ไลบรารีเวลา Java Time4Jผ่านตัวอย่างนี้:
MomentInterval a = MomentInterval.between(Instant.now(), Instant.now().plusSeconds(2));
MomentInterval b = a.collapse(); // make b an empty interval out of a
System.out.println(a); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:13,909000000Z)
System.out.println(b); // [2017-04-10T05:28:11,909000000Z/2017-04-10T05:28:11,909000000Z)
เครื่องหมายวงเล็บเหลี่ยมนำหน้า "[" หมายถึงจุดเริ่มต้นที่ปิดในขณะที่เครื่องหมายวงเล็บสุดท้าย ")" หมายถึงปลายเปิด
System.out.println(
"startA < endB: " + a.getStartAsInstant().isBefore(b.getEndAsInstant())); // false
System.out.println(
"endA > startB: " + a.getEndAsInstant().isAfter(b.getStartAsInstant())); // true
System.out.println("a overlaps b: " + a.intersects(b)); // a overlaps b: false
ดังที่แสดงไว้ข้างต้นช่วงเวลาที่ว่างเปล่าละเมิดเงื่อนไขการทับซ้อนด้านบน (โดยเฉพาะ startA <endB) ดังนั้น Time4J (และไลบรารี่อื่น ๆ เช่นกัน) ต้องจัดการเป็นกรณีขอบพิเศษเพื่อรับประกันว่าการทับซ้อนของช่วงเวลาใด ๆ ไม่ได้อยู่. แน่นอนช่วงเวลาวันที่ (ซึ่งถูกปิดโดยค่าเริ่มต้นใน Time4J แต่สามารถเปิดได้ครึ่งหนึ่งเช่นช่วงวันที่ว่างเปล่า) ได้รับการจัดการในลักษณะเดียวกัน
นี่เป็นวิธีการทั่วไปที่สามารถใช้ได้ในพื้นที่
// Takes a list and returns all records that have overlapping time ranges.
public static IEnumerable<T> GetOverlappedTimes<T>(IEnumerable<T> list, Func<T, bool> filter, Func<T,DateTime> start, Func<T, DateTime> end)
{
// Selects all records that match filter() on left side and returns all records on right side that overlap.
var overlap = from t1 in list
where filter(t1)
from t2 in list
where !object.Equals(t1, t2) // Don't match the same record on right side.
let in1 = start(t1)
let out1 = end(t1)
let in2 = start(t2)
let out2 = end(t2)
where in1 <= out2 && out1 >= in2
let totover = GetMins(in1, out1, in2, out2)
select t2;
return overlap;
}
public static void TestOverlap()
{
var tl1 = new TempTimeEntry() { ID = 1, Name = "Bill", In = "1/1/08 1:00pm".ToDate(), Out = "1/1/08 4:00pm".ToDate() };
var tl2 = new TempTimeEntry() { ID = 2, Name = "John", In = "1/1/08 5:00pm".ToDate(), Out = "1/1/08 6:00pm".ToDate() };
var tl3 = new TempTimeEntry() { ID = 3, Name = "Lisa", In = "1/1/08 7:00pm".ToDate(), Out = "1/1/08 9:00pm".ToDate() };
var tl4 = new TempTimeEntry() { ID = 4, Name = "Joe", In = "1/1/08 3:00pm".ToDate(), Out = "1/1/08 8:00pm".ToDate() };
var tl5 = new TempTimeEntry() { ID = 1, Name = "Bill", In = "1/1/08 8:01pm".ToDate(), Out = "1/1/08 8:00pm".ToDate() };
var list = new List<TempTimeEntry>() { tl1, tl2, tl3, tl4, tl5 };
var overlap = GetOverlappedTimes(list, (TempTimeEntry t1)=>t1.ID==1, (TempTimeEntry tIn) => tIn.In, (TempTimeEntry tOut) => tOut.Out);
Console.WriteLine("\nRecords overlap:");
foreach (var tl in overlap)
Console.WriteLine("Name:{0} T1In:{1} T1Out:{2}", tl.Name, tl.In, tl.Out);
Console.WriteLine("Done");
/* Output:
Records overlap:
Name:Joe T1In:1/1/2008 3:00:00 PM T1Out:1/1/2008 8:00:00 PM
Name:Lisa T1In:1/1/2008 7:00:00 PM T1Out:1/1/2008 9:00:00 PM
Done
*/
}
public static class NumberExtensionMethods
{
public static Boolean IsBetween(this Int64 value, Int64 Min, Int64 Max)
{
if (value >= Min && value <= Max) return true;
else return false;
}
public static Boolean IsBetween(this DateTime value, DateTime Min, DateTime Max)
{
Int64 numricValue = value.Ticks;
Int64 numericStartDate = Min.Ticks;
Int64 numericEndDate = Max.Ticks;
if (numricValue.IsBetween(numericStartDate, numericEndDate) )
{
return true;
}
return false;
}
}
public static Boolean IsOverlap(DateTime startDate1, DateTime endDate1, DateTime startDate2, DateTime endDate2)
{
Int64 numericStartDate1 = startDate1.Ticks;
Int64 numericEndDate1 = endDate1.Ticks;
Int64 numericStartDate2 = startDate2.Ticks;
Int64 numericEndDate2 = endDate2.Ticks;
if (numericStartDate2.IsBetween(numericStartDate1, numericEndDate1) ||
numericEndDate2.IsBetween(numericStartDate1, numericEndDate1) ||
numericStartDate1.IsBetween(numericStartDate2, numericEndDate2) ||
numericEndDate1.IsBetween(numericStartDate2, numericEndDate2))
{
return true;
}
return false;
}
if (IsOverlap(startdate1, enddate1, startdate2, enddate2))
{
Console.WriteLine("IsOverlap");
}
ใช้ Java util.Date นี่คือสิ่งที่ฉันทำ
public static boolean checkTimeOverlaps(Date startDate1, Date endDate1, Date startDate2, Date endDate2)
{
if (startDate1 == null || endDate1 == null || startDate2 == null || endDate2 == null)
return false;
if ((startDate1.getTime() <= endDate2.getTime()) && (startDate2.getTime() <= endDate1.getTime()))
return true;
return false;
}
วิธีที่ง่ายที่สุดที่จะทำในความคิดของฉันคือการเปรียบเทียบว่า EndDate1 ใดอยู่ก่อนหน้า StartDate2 และ EndDate2 ก่อนหน้า StartDate1
แน่นอนว่าถ้าคุณกำลังพิจารณาช่วงเวลาที่ StartDate อยู่ก่อน EndDate
ฉันมีสถานการณ์ที่เรามีวันที่แทนที่จะเป็นชุดข้อมูลและวันที่สามารถทับซ้อนกันเฉพาะเมื่อเริ่มต้น / สิ้นสุด ตัวอย่างด้านล่าง:
(สีเขียวคือช่วงเวลาปัจจุบันบล็อกสีน้ำเงินเป็นช่วงเวลาที่ถูกต้องส่วนสีแดงคือช่วงเวลาที่ทับซ้อนกัน)
ฉันปรับคำตอบของ Ian Nelson กับวิธีแก้ไขปัญหาต่อไปนี้:
(startB <= startA && endB > startA)
|| (startB >= startA && startB < endA)
กรณีนี้ตรงกับกรณีที่ทับซ้อนกันทั้งหมด แต่ไม่สนใจกรณีที่ทับซ้อนกันที่อนุญาต
แยกปัญหาออกเป็นกรณีแล้วจัดการกับแต่ละกรณี
สถานการณ์ 'ช่วงวันที่สองจุดตัด' ถูกครอบคลุมโดยสองกรณี - ช่วงวันแรกเริ่มต้นภายในช่วงที่สองหรือช่วงวันที่ที่สองเริ่มต้นภายในช่วงแรก
คุณสามารถลองสิ่งนี้:
//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);
นี่คือโซลูชันของฉันมันจะคืนค่าจริงเมื่อค่าไม่ทับซ้อนกัน:
X เริ่มต้น 1 ปีสิ้นสุด 1
A เริ่มต้น 2 B สิ้นสุด 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
สำหรับทับทิมฉันก็พบสิ่งนี้ด้วย:
class Interval < ActiveRecord::Base
validates_presence_of :start_date, :end_date
# Check if a given interval overlaps this interval
def overlaps?(other)
(start_date - other.end_date) * (other.start_date - end_date) >= 0
end
# Return a scope for all interval overlapping the given interval, including the given interval itself
named_scope :overlapping, lambda { |interval| {
:conditions => ["id <> ? AND (DATEDIFF(start_date, ?) * DATEDIFF(?, end_date)) >= 0", interval.id, interval.end_date, interval.start_date]
}}
end
พบที่นี่พร้อมคำอธิบายที่ดี -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails
ข้อความค้นหาด้านล่างให้รหัสฉันซึ่งช่วงวันที่ที่ให้ไว้ (วันที่เริ่มต้นและวันที่สิ้นสุดทับซ้อนกับวันใด ๆ (วันที่เริ่มต้นและวันที่สิ้นสุด) ใน table_name ของฉัน
select id from table_name where (START_DT_TM >= 'END_DATE_TIME' OR
(END_DT_TM BETWEEN 'START_DATE_TIME' AND 'END_DATE_TIME'))