web-dev-qa-db-fra.com

Comment vérifier l'intersection des périodes DateTime

J'ai quatre objets DateTime . A1, A2 et B1, B2.

J'ai besoin de savoir que la période A1-A2 ne recoupe pas la période B1-B2. Mais je ne veux pas écrire de code sale, comme beaucoup de blocs.

if (A1 < B1 && A2 > B1)
{
    return false;
}

....etc.

ÉDITÉ

J'ai essayé d'utiliser celui-ci: Comparaison de gammes

DateTime A1 = DateTime.MinValue.AddMinutes(61);
DateTime A2 = DateTime.MinValue.AddHours(1.2);
DateTime B1 = DateTime.MinValue.AddMinutes(5);
DateTime B2 = DateTime.MinValue.AddHours(1);

Console.WriteLine(Range.Overlap(
    new Range<DateTime>(A1, A2),
    new Range<DateTime>(B1, B2)
));

Il a retourné true mais je m'attendais à false . Parce que ce code retourne toujours true

 if (left.Start.CompareTo(left.Start) == 0)
 {
     return true;
 }
28
Ivan Korytin

Je ne crois pas qu'il y aura une quelconque manière de code "facile" à écrire; vous devez tenir compte de 4 cas d'utilisation distincts. Si vous devez souvent faire ce type de vérification, j'écrirais une méthode d'extension. Sinon, il vous suffit de vérifier ces conditions:

 |--- Date 1 ---|
      | --- Date 2 --- |


      | --- Date 1 --- |
 | --- Date 2 ---- |


 | -------- Date 1 -------- |
      | --- Date 2 --- |

      | --- Date 1 --- |
 | -------- Date 2 -------- |

EDIT: Pour fournir le code actuel:

public class DateTimeRange
{
     public DateTime Start { get; set; }
     public DateTime End { get; set; }

     public bool Intersects(DateTimeRange test)
     {
         if(this.Start > this.End || test.Start > test.End)
            throw new InvalidDateRangeException();

         if(this.Start == this.End || test.Start == test.End)
              return false; // No actual date range

         if(this.Start == test.Start || this.End == test.End)
              return true; // If any set is the same time, then by default there must be some overlap. 

         if(this.Start < test.Start)
         {
              if(this.End > test.Start && this.End < test.End)
                  return true; // Condition 1

              if(this.End > test.End)
                  return true; // Condition 3
         }
         else
         {
              if(test.End > this.Start && test.End < this.End)
                  return true; // Condition 2

              if(test.End > this.End)
                  return true; // Condition 4
         }

         return false;
    }
}

Cela devrait couvrir les cas d'utilisation. 

35
Tejs

Si dans votre programme, les plages A1-A2 et B1-B2 sont "correctes" en ce sens que l'on sait que A1 <= A2 et B1 <= B2

alors votre test de non-intersection est tout simplement

if(A1>B2 || B1>A2)

Remarque J'ai passé sous silence s'il s'agit de> ou> =. Le choix approprié de l'opérateur dépend de la manière dont vous avez défini vos plages pour inclure ou exclure leurs points d'extrémité. c'est-à-dire s'ils représentent des intervalles fermés, ouverts ou semi-ouverts.

41
Corey Kosak

Bibliothèque de périodes pour .NET a l’air intéressant.

Des méthodes telles que IsSamePeriod, HasInside, OverlapsWith ou IntersectsWith sont disponibles pour faciliter la recherche de variantes spéciales souvent utilisées de ces relations de période.

24
Dave Ziegler

Mon approche consiste à créer une classe appelée Period qui contient les propriétés Start et End (DateTime). Cette classe peut avoir des méthodes ou des méthodes d'extension pour calculer des choses telles que les intersections. Disons que vous avez une méthode comme celle-ci dans votre classe de période:

public bool IntersectsWith(Period otherPeriod)
{
    return !(this.Start > otherPeriod.End || this.End < otherPeriod.Start);
}

Ensuite, vous pouvez écrire le code comme ceci:

if (!periodA.IntersectsWith(periodB))
{
    return false;
}
15
Meta-Knight

Le code que vous avez essayé avait un bug, je l'ai corrigé:

Essaye ça:

class Range<T> where T : IComparable<T>
{
    public T Start { get; private set;}
    public T End { get; private set;}

    public Range(T start, T end)
    {
        //Always ensure that Start < End
        if(start.CompareTo(end) >= 0)
        {
            var temp = end;
            end = start;
            start = temp;
        }

        Start = start;
        End = end;
    }
}

static class Range
{
    //Based on Eric's idea of doing negative check to figure out
    //how many ways there are for ranges to NOT overlap.
    public static bool EricOverlap<T>(Range<T> left, Range<T> right)
        where T : IComparable<T>
    {
        if (right.Start.CompareTo(left.End) > 0)
            return false;

        if (left.Start.CompareTo(right.End) > 0)
            return false;

        return true;
    }
    public static bool Overlap<T>(Range<T> left, Range<T> right)
        where T : IComparable<T>
    {
        if (left.Start.CompareTo(right.Start) == 0)
        {
            return true;
        }

        else if (left.Start.CompareTo(right.Start) > 0)
        {
            return left.Start.CompareTo(right.End) <= 0;
        }
        else
        {
            return right.Start.CompareTo(left.End) <= 0;
        }
    }
}
5
SolutionYogi

Cette classe de tests unitaires accompagne la solution ci-dessus avec Tejs en utilisant la classe DateTimeRange (constructeur modifié). Sa solution est correcte et ces tests le prouvent (au cas où vous voudriez copier en production. :))

[TestClass]
public class DateTimeRangeTests
{
    [TestMethod]
    public void overlap_dates_is_interscected_second_newer_test()
    {
        //|--- Date 1 ---|
        //    | --- Date 2 --- |
        DateTime baseTime = DateTime.Now;
        var r1 = new DateTimeRange(baseTime.AddDays(-4), baseTime.AddDays(-2));
        var r2 = new DateTimeRange(baseTime.AddDays(-3), baseTime.AddDays(-1));

        Assert.IsTrue(r1.Intersects(r2));
    }

    [TestMethod]
    public void overlap_dates_is_interscected_second_older_test()
    {
        //        |--- Date 1 ---|
        //    | --- Date 2 --- |
        DateTime baseTime = DateTime.Now;
        var r1 = new DateTimeRange(baseTime.AddDays(-3), baseTime.AddDays(-1));
        var r2 = new DateTimeRange(baseTime.AddDays(-4), baseTime.AddDays(-2));

        Assert.IsTrue(r1.Intersects(r2));
    }

    [TestMethod]
    public void overlap_dates_is_interscected_second_subset_of_first_test()
    {
        //| -------- Date 1 -------- |
        //   | --- Date 2 --- |
        DateTime baseTime = DateTime.Now;
        var r1 = new DateTimeRange(baseTime.AddDays(-4), baseTime.AddDays(-1));
        var r2 = new DateTimeRange(baseTime.AddDays(-3), baseTime.AddDays(-2));

        Assert.IsTrue(r1.Intersects(r2));
    }

    [TestMethod]
    public void overlap_dates_is_interscected_second_superset_of_first_test()
    {
        //| -------- Date 1 -------- |
        //   | --- Date 2 --- |
        DateTime baseTime = DateTime.Now;
        var r1 = new DateTimeRange(baseTime.AddDays(-3), baseTime.AddDays(-2));
        var r2 = new DateTimeRange(baseTime.AddDays(-4), baseTime.AddDays(-1));

        Assert.IsTrue(r1.Intersects(r2));
    }

    [TestMethod]
    public void non_intersects_dates_when_second_before_first_test()
    {
        //                        | --- Date 1 -------- |
        //   | --- Date 2 --- |
        DateTime baseTime = DateTime.Now;
        var r1 = new DateTimeRange(baseTime.AddDays(-1), baseTime.AddDays(0));
        var r2 = new DateTimeRange(baseTime.AddDays(-4), baseTime.AddDays(-2));

        Assert.IsFalse(r1.Intersects(r2));
    }

    [TestMethod]
    public void non_intersects_dates_when_second_after_first_test()
    {
        //   | --- Date 1 ------ |
        //                          | --- Date 2 --- |
        DateTime baseTime = DateTime.Now;
        var r1 = new DateTimeRange(baseTime.AddDays(-4), baseTime.AddDays(-2));
        var r2 = new DateTimeRange(baseTime.AddDays(-1), baseTime.AddDays(-0));

        Assert.IsFalse(r1.Intersects(r2));
    }
}
2
Michael Kennedy

Aucun moyen de le contourner:

* Édité pour simplification:

En supposant que B2> B1 et A2> A1:

if (A2 >= B1 && A1 <= B2) {
    // some part of a1-a2 is in b1-b2
}

Cela permettra de détecter si une partie de A1-A2 est dans B1-B2.

Si vous devez détecter si A1-A2 est complètement dans B1-B2:

if (B1 <= A1 && B2 >= A2) {
    // all of a1-a2 is in b1-b2
}
2
Jonathan M

Je pense que vous pouvez le faire comme ça! ((Fin2 <début1) || (début2> fin1)):

DateTime start1 = new DateTime(1);
DateTime end1 = new DateTime(2);

DateTime start2 = new DateTime(1);
DateTime end2 = new DateTime(2);

Console.WriteLine(!( (end2 < start1) || (start2 > end1) )); //returns true

[OU]

DateTime start1 = new DateTime(1);
DateTime end1 = new DateTime(2);

DateTime start2 = new DateTime(3);
DateTime end2 = new DateTime(4);


Console.WriteLine(!( (end2 < start1) || (start2 > end1) )); // returns false
1
Cubicle.Jockey
public bool Overlap(DateTime startDate1, DateTime endDate1, DateTime startDate2, DateTime endDate2)
        {
            if (endDate1 >= startDate2 && endDate2 >= startDate1)
            {
                return true;
            }

            if (startDate1 <= endDate2 && startDate2 <= startDate1)
            {
                return true;
            }

            return false;
        }
1
Acaz Souza

Pourquoi ne pas vérifier si vos périodes ne se chevauchent PAS? Ensuite, si la condition de non-chevauchement est fausse, cela signifie qu'elles se chevauchent:

    bool NotOverlapping = (start1 < start2 && end1 < start2) || (start1 > end2 && end1 > end2); 
    return !NotOverlapping // !NotOverlapping == Overlapping
0
Muryan
DateTime[] start = new DateTime[] { new DateTime(2000, 1, 1), new DateTime(2004, 1, 1),   
                                  new DateTime(2004, 1, 1), new DateTime(2008, 1, 1) };  
                 /*date that start from*/

DateTime[] end   = new DateTime[] { new DateTime(2002, 1, 1), new DateTime(2006, 1, 1),  
new DateTime(2006, 1, 1), new DateTime(2010, 1, 1) }; /*date that end */

int timeDifference ; 

TimeSpan timespan;

 /*buttonclick  */
    {     /*find total days which note overlap*/
    for (int i=0; i<end.Length-2; i++)
    {        
        if (end[i] < end[i + 1] && start[i] < start[i + 1] && start[i + 1] >= end[i])
            {
                timespan = (end[i] - start[i]) + (end[i + 1] - end[i]);        
    }

        if (end[i] >= end[i + 1] && start[i] <= start[i + 1])          
            {
                timespan = (end[i] - start[i]);            
        }
        if (end[i] > end[i + 1] && start[i] > start[i + 1] && start[i] <= end[i + 1])         
            {
                timespan = (end[i] - start[i]) + (end[i + 1] - end[i]);
            }
        if  (end[i] <= end[i + 1] && start[i] >= start[i + 1])    
            {        
                    timespan = (end[i + 1] - start[i + 1]);
            }

            timeDifference = timespan.Days + timeDifference;                          

        } 
              MessageBox.Show(timeDifference.ToString()); 
  }
        }}
0
user2951991