datetime - निर्धारित करें कि दो दिनांक सीमा ओवरलैप है या नहीं




math language-agnostic (20)

दो तारीख सीमाओं को देखते हुए, यह निर्धारित करने का सबसे सरल या सबसे प्रभावी तरीका क्या है कि दो दिनांक सीमाएं ओवरलैप हैं या नहीं?

उदाहरण के तौर पर, मान लीजिए कि हमारे पास डेटटाइम वेरिएबल्स StartDate1 से EndDate1 और StartDate2 को EndDate2 तक EndDate2

https://code.i-harness.com


सबसे आसान

सबसे आसान तरीका डेट-टाइम काम के लिए एक अच्छी तरह से इंजीनियर समर्पित पुस्तकालय का उपयोग करना है।

someInterval.overlaps( anotherInterval )

जावा। टाइम और थ्रीटेन-अतिरिक्त

व्यवसाय में सबसे अच्छा जावा 8 और बाद में जावा java.time फ्रेमवर्क बनाया गया है। ThreeTen-Extra परियोजना जो अतिरिक्त कक्षाओं के साथ जावा.टाइम को पूरक करती है, विशेष रूप से Interval वर्ग जिसे हमें यहां चाहिए।

इस प्रश्न पर language-agnostic टैग के लिए, दोनों परियोजनाओं के लिए स्रोत कोड अन्य भाषाओं में उपयोग के लिए उपलब्ध है (उनके लाइसेंस दिमाग)।

Interval

Interval कक्षा आसान है, लेकिन तारीख-केवल मूल्यों के बजाय दिनांक-समय के क्षणों ( java.time.Instant ऑब्जेक्ट्स) की आवश्यकता होती है। तो हम तिथि का प्रतिनिधित्व करने के लिए यूटीसी में दिन के पहले पल का उपयोग करके आगे बढ़ते हैं।

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

हम एक प्रारंभिक पल और एक Duration साथ Interval को भी परिभाषित कर सकते हैं।

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 दृष्टिकोण का उपयोग करते हैं जहां शुरुआत समावेशी है और अंत अनन्य है


@ ब्रेटाना द्वारा दिए गए गणितीय समाधान अच्छे हैं लेकिन दो विशिष्ट विवरणों की उपेक्षा करते हैं:

  1. बंद या आधे खुले अंतराल के पहलू
  2. खाली अंतराल

अंतराल सीमाओं की बंद या खुली स्थिति के बारे में, @ अंतराल का समाधान बंद अंतराल के लिए मान्य है

(स्टार्ट ए <= एंडबी) और (एंडए> = स्टार्टबी)

आधे खुले अंतराल के लिए फिर से लिखा जा सकता है:

(स्टार्ट ए <एंडबी) और (एंड ए> स्टार्टबी)

यह सुधार आवश्यक है क्योंकि एक खुली अंतराल सीमा परिभाषा के अंतराल की मान सीमा से संबंधित नहीं है।

और खाली अंतराल के बारे में, ठीक है, ऊपर दिखाया गया रिश्ता पकड़ नहीं है। खाली अंतराल जिसमें परिभाषा के अनुसार कोई वैध मान नहीं है, विशेष मामले के रूप में संभाला जाना चाहिए। मैं इसे अपने जावा टाइम लाइब्रेरी टाइम 4 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

As shown above, empty intervals violate the overlap condition above (especially startA < endB), so Time4J (and other libraries, too) has to handle it as special edge case in order to guarantee that the overlap of any arbitrary interval with an empty interval does not exist. Of course, date intervals (which are closed by default in Time4J but can be half-open, too, like empty date intervals) are handled in a similar way.


अस्थायी संबंधों (या किसी अन्य अंतराल संबंधों के बारे में तर्क के लिए, उस पर आना), एलन के अंतराल बीजगणित पर विचार करें। यह 13 संभावित संबंधों का वर्णन करता है कि दो अंतराल एक-दूसरे के संबंध में हो सकते हैं। आप अन्य संदर्भ पा सकते हैं - "एलन अंतराल" एक ऑपरेटिव खोज शब्द प्रतीत होता है। आप इन परिचालनों के बारे में जानकारी एसक्यूएलग्रैस के विकास समय-उन्मुख अनुप्रयोगों में एसक्यूएल (पीडीएफ ऑनलाइन यूआरएल पर ऑनलाइन उपलब्ध), और डेट, डार्वेन और लोरेंटेजोस टेम्पोरल डेटा और रिलेशनल मॉडल (2002) या समय और रिलेशनल थ्योरी में: अस्थायी डेटाबेस में इन परिचालनों के बारे में जानकारी भी प्राप्त कर सकते हैं : रिलेशनल मॉडल और एसक्यूएल (2014; प्रभावी रूप से टीडी और आरएम का दूसरा संस्करण)।

छोटा (आईएसएच) उत्तर है: दो दिनांक अंतराल A और B को घटकों के साथ दिया गया है। .start और .end और बाधा .start <= .end , फिर दो अंतराल ओवरलैप करें यदि:

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

आप ओवरलैप की डिग्री के लिए अपनी आवश्यकताओं को पूरा करने के लिए >= बनाम > और <= बनाम < के उपयोग को ट्यून कर सकते हैं।

एरिक टिप्पणियां:

यदि आप चीजों को मजाकिया मानते हैं तो आप केवल 13 प्राप्त कर सकते हैं ... जब मैं इसके साथ पागल हो जाता हूं तो मुझे "दो संभावित अंतराल मिल सकते हैं"। समझदार गिनती से, मुझे केवल छः मिलते हैं, और यदि आप देखभाल करते हैं कि ए या बी पहले आता है, तो मुझे केवल तीन मिलते हैं (कोई अंतर नहीं, आंशिक रूप से अंतरण, एक पूरी तरह से दूसरे के भीतर)। 15 इस तरह से चला जाता है: [पहले: पहले, शुरू, भीतर, अंत, बाद में], [प्रारंभ: शुरू, भीतर, अंत, बाद], [भीतर: भीतर, अंत, बाद], [अंत: अंत, बाद], [ के बाद: के बाद]।

मुझे लगता है कि आप पहले से पहले और बाद में 'बाद में' दो प्रविष्टियों की गणना नहीं कर सकते हैं। यदि आप अपने इनवर्ड्स के साथ कुछ संबंधों को समझाते हैं तो मैं 7 प्रविष्टियों को देख सकता हूं (संदर्भित विकिपीडिया यूआरएल में आरेख देखें; इसमें 7 प्रविष्टियां हैं, जिनमें से 6 में एक अलग उलटा है, जिसमें एक अलग विपरीत नहीं है)। और क्या तीन समझदार है आपकी आवश्यकताओं पर निर्भर करता है।

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

आप इसे आजमा सकते हैं:

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

माइक्रोसॉफ्ट एसक्यूएल सर्वर - एसक्यूएल फंक्शन में

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

मुझे क्या करना होगा

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

मेरा मानना ​​है कि यह कहना पर्याप्त है कि दो श्रेणियां ओवरलैप हैं यदि:

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

मेरी राय में ऐसा करने का सबसे आसान तरीका यह तुलना करना होगा कि अगर EndDate1 StartDate2 से पहले है और EndDate2 StartDate1 से पहले है।

बेशक यदि आप अंतराल पर विचार कर रहे हैं जहां StartDate हमेशा EndDate से पहले है।


यदि आप एक तिथि सीमा का उपयोग कर रहे हैं जो अभी तक समाप्त नहीं हुआ है (अभी भी चल रहा है) उदाहरण के लिए endDate = '0000-00-00' सेट नहीं है, तो आप 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 अधिक है तो अंत में कोई ओवरलैप नहीं है!


यदि ओवरलैप की गणना भी की जानी चाहिए, तो आप निम्न सूत्र का उपयोग कर सकते हैं:

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

यह आलेख .NET के लिए समय अवधि लाइब्रेरी गणना अवधि द्वारा दो समयावधि के संबंध का वर्णन करता है:

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


यह पल.जेएस के साथ मेरा जावास्क्रिप्ट समाधान था:

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

यहां पोस्ट किया गया समाधान सभी ओवरलैपिंग श्रेणियों के लिए काम नहीं करता है ...

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

मेरा कामकाजी समाधान था:

AND (
  ('start_date' BETWEEN STARTDATE AND ENDDATE) -- caters for inner and end date outer
  OR
  ('end_date' BETWEEN STARTDATE AND ENDDATE) -- caters for inner and start date outer
  OR
  (STARTDATE BETWEEN 'start_date' AND 'end_date') -- only one needed for outer range where dates are inside.
) 

सभी समाधान जो कि एक दूसरे के संबंध में श्रेणियों के आधार पर स्थितियों की एक बड़ी संख्या की जांच करते हैं, यह सुनिश्चित करके कि एक विशिष्ट सीमा पहले शुरू होती है, यह बहुत सरल हो सकती है ! आप सुनिश्चित करते हैं कि पहली श्रेणी सीमाओं को स्वैप करके पहले (या साथ ही) पहले शुरू हो जाती है।

फिर, यदि आप दूसरी श्रेणी की शुरुआत पहली श्रेणी के अंत से कम या बराबर होती हैं (यदि श्रेणियां समावेशी हैं, जिसमें प्रारंभ और समाप्ति समय दोनों शामिल हैं) या उससे कम (यदि श्रेणियां प्रारंभ और अंत के अनन्य शामिल हैं) से ओवरलैप का पता लगा सकते हैं। ।

दोनों सिरों पर समावेशी मानते हुए, केवल चार संभावनाएं हैं जिनमें से एक गैर-ओवरलैप है:

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

आप चेक की संख्या को बहुत सीमित करते हैं क्योंकि आप रेंज 2 के बाद कभी भी शुरू नहीं होने के कारण समस्या का आधा हिस्सा निकाल देते हैं।


जावा में मेरा समाधान यहां है, जो असंबद्ध अंतराल पर भी काम करता है

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

(स्टार्ट ए <= एंडबी) और (एंडए> = स्टार्टबी)

प्रमाण:
कंडीशनए का मतलब है कि तिथि सीमा के बाद डेटरेंज पूरी तरह से दिनांक बी
_ |---- DateRange A ------| |---Date Range B -----| _
(यदि StartA > EndB ) सही है

कंडीशनबी का मतलब है कि डेटरेंज ए डेटरेंज बी से पहले पूरी तरह से है
|---- DateRange A -----| _ _ |---Date Range B ----|
(सच है अगर EndA < StartB )

फिर ओवरलैप मौजूद है यदि न तो ए और बी सत्य है -
(यदि एक सीमा न तो दूसरे के बाद पूरी तरह से है,
न ही दूसरे से पहले, फिर उन्हें ओवरलैप करना होगा।)

अब डी मॉर्गन के कानूनों में से एक का कहना है कि:

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

जो अनुवाद करता है: (StartA <= EndB) and (EndA >= StartB)

नोट: इसमें ऐसी स्थितियां शामिल हैं जहां किनारों को बिल्कुल ओवरलैप किया जाता है। अगर आप इसे बाहर करना चाहते हैं,
>= ऑपरेटरों को > और <= to < बदलें

नोट 2। @ बाओदाद के लिए धन्यवाद, इस ब्लॉग को देखें, वास्तविक ओवरलैप निम्न में से कम है:
{ endB-startA , endB - startB , endB-startA , endB - startB }

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

नोट 3। @ टॉमोसियस के लिए धन्यवाद, एक छोटा संस्करण पढ़ता है:
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)


For ruby I also found this:

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

Found it here with nice explaination -> http://makandracards.com/makandra/984-test-if-two-date-ranges-overlap-in-ruby-or-rails


This was my solution, it returns true when the values don't overlap:

X START 1 Y END 1

A START 2 B END 2

TEST1: (X <= A || X >= B)
        &&
TEST2: (Y >= B || Y <= A) 
        && 
TEST3: (X >= B || Y <= A)


X-------------Y
    A-----B

TEST1:  TRUE
TEST2:  TRUE
TEST3:  FALSE
RESULT: FALSE

---------------------------------------

X---Y
      A---B

TEST1:  TRUE
TEST2:  TRUE
TEST3:  TRUE
RESULT: TRUE

---------------------------------------

      X---Y
A---B

TEST1:  TRUE
TEST2:  TRUE
TEST3:  TRUE
RESULT: TRUE

---------------------------------------

     X----Y
A---------------B

TEST1:  FALSE
TEST2:  FALSE
TEST3:  FALSE
RESULT: FALSE

नीचे प्रश्न मुझे उन आईडी को देता है जिनके लिए आपूर्ति की गई तिथि सीमा (प्रारंभ और समाप्ति तिथियां मेरी तालिका_नाम में किसी भी तारीख (प्रारंभ और समाप्ति तिथियों) के साथ ओवरलैप होती हैं

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

if (StartDate1 > StartDate2) swap(StartDate, EndDate);

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




language-agnostic