2010-09-24 20 views
9

Ho la seguente classe:Verificare se un intervallo di date è all'interno di un intervallo di date

public class Membership 
{ 
    public DateTime StartDate { get; set; } 
    public DateTime? EndDate { get; set; } // If null then it lasts forever 
} 

ho bisogno di assicurarsi che quando si aggiunge il seguente elenco che il nuovo elemento non si sovrappone le date da elemento esistente :

var membership = new List<Membership> 
{ 
    new Membership { StartDate = DateTime.UtcNow.AddDays(-10), EndDate = DateTime.UtcNow.AddDays(-5) }, 
    new Membership { StartDate = DateTime.UtcNow.AddDays(-5), EndDate = null } 
}; 

Ad esempio fare:

var newItem = new Membership { StartDate = DateTime.UtcNow.AddDays(-15), EndDate = DateTime.UtcNow.AddDays(-10) }; // Allowed 

var newItem2 = new Membership { StartDate = DateTime.UtcNow.AddDays(-15), EndDate = null }; // Not Allowed 

if (AllowededToAdd(newItem)) 
    membership.Add(newItem); 

if (AllowededToAdd(newItem2)) 
    membership.Add(newItem2); 

ho pensato che questo sarebbe stato semplice, ma finora i miei tentativi sono stati tutti WRO e sto iniziando a confondermi e speravo che qualcuno avesse fatto qualcosa di simile che potevano condividere. Grazie

risposta

14

Fondamentalmente, un intervallo di date sovrappone un'altra se qualsiasi delle sue terminazioni sono all'interno dell'altro intervallo, o viceversa.

static bool AllowedToAdd(List<Membership> membershipList, Membership newItem) 
{ 
    return !membershipList.Any(m => 
     (m.StartDate < newItem.StartDate && 
     newItem.StartDate < (m.EndDate ?? DateTime.MaxValue)) 
     || 
     (m.StartDate < (newItem.EndDate ?? DateTime.MaxValue) && 
     (newItem.EndDate ?? DateTime.MaxValue) <= (m.EndDate ?? DateTime.MaxValue)) 
     || 
     (newItem.StartDate < m.StartDate && 
     m.StartDate < (newItem.EndDate ?? DateTime.MaxValue)) 
     || 
     (newItem.StartDate < (m.EndDate ?? DateTime.MaxValue) && 
     (m.EndDate ?? DateTime.MaxValue) <= (newItem.EndDate ?? DateTime.MaxValue)) 
     ); 
} 

Con l'utilizzo:

if (AllowedToAdd(membershipList, newItem)) 
    membershipList.Add(newItem); 
+0

Grazie per le risposte a tutti, ma preferisco questo perché è il più facile da capire lol. – nfplee

1

Una condizione come questo dovrebbe fare il trucco:

newItem.StartDate <= range.EndDate && newItem.EndDate.HasValue && newItem.EndDate >= range.StartDate 
+0

Questa è una soluzione molto logica e geniale. Thnx @Joachim VR –

5

Quindi, se ho capito bene - si vuole fare in modo intervallo di date 2 non rientra nell'intervallo di date 1?

Ad esempio:

startDate1 = 01/01/2011 

endDate1 = 01/02/2011 

e

startDate2 = 19/01/2011 

endDate2 = 10/02/2011 

Questo dovrebbe essere un semplice caso di:

if ((startDate2 >= startDate1 && startDate2 <= endDate1) || 
    (endDate2 >= startDate1 && endDate2 <= endDate1)) 
+0

si dovrebbe anche verificare il valore nullo. – jimplode

2

Ecco una soluzione (manca null argomento della validazione, e la validazione entro Membership che EndDate > StartDate) utilizzando Collection<T>:

public class Membership 
{ 
    public DateTime StartDate { get; set; } 
    public DateTime? EndDate { get; set; } // If null then it lasts forever 

    private DateTime NullSafeEndDate { get { return EndDate ?? DateTime.MaxValue; } } 

    private bool IsFullyAfter(Membership other) 
    { 
     return StartDate > other.NullSafeEndDate; 
    } 

    public bool Overlaps(Membership other) 
    { 
     return !IsFullyAfter(other) && !other.IsFullyAfter(this); 
    } 
} 


public class MembershipCollection : Collection<Membership> 
{ 
    protected override void InsertItem(int index, Membership member) 
    { 
     if(CanAdd(member)) 
      base.InsertItem(index, member); 
     else throw new ArgumentException("Ranges cannot overlap."); 
    } 

    public bool CanAdd(Membership member) 
    { 
     return !this.Any(member.Overlaps); 
    } 
} 
0

Se non lo fai avere criteri diversi per l'ordinamento, quindi iniziare mantenendo l'ordine in ordine. Poiché nessun oggetto aggiunto in precedenza può sovrapporsi, una volta compreso il punto in cui aggiungere un nuovo oggetto, è necessario confrontare solo i singoli oggetti su entrambi i lati per assicurarsi che il nuovo oggetto sia consentito. È inoltre necessario considerare se la data di fine dell'oggetto "precedente" si sovrappone alla data di inizio dell'oggetto "successivo", poiché questo ordine rende irrilevante l'altra possibilità di sovrapposizione.

Quindi, oltre a semplificare la questione della rilevazione di sovrapposizioni, possiamo ridurre la complessità da O (n) a O (log n), come piuttosto che confrontare con tutti gli articoli esistenti, confrontiamo con 0-2 noi ' ho trovato attraverso una ricerca O (log n).

private class MembershipComparer : IComparer<Membership> 
{ 
    public int Compare(Membership x, Membership y) 
    { 
    return x.StartDate.CompareTo(y.StartDate); 
    } 
} 
private static bool AddMembership(List<Membership> lst, Membership ms) 
{ 
    int bsr = lst.BinarySearch(ms, new MembershipComparer()); 
    if(bsr >= 0) //existing object has precisely the same StartDate and hence overlaps 
        //(you may or may not want to consider the case of a zero-second date range) 
    return false; 
    int idx = ~bsr; //index to insert at if doesn't match already. 
    if(idx != 0) 
    { 
    Membership prev = lst[idx - 1]; 
    // if inclusive ranges is allowed (previous end precisely the same 
    // as next start, change this line to: 
    // if(!prev.EndDate.HasValue || prev.EndDate > ms.StartDate) 
    if(prev.EndDate ?? DateTime.MaxValue >= ms.StartDate) 
     return false; 
    } 
    if(idx != lst.Count) 
    { 
    Membership next = lst[idx]; 
    // if inclusive range is allowed, change to: 
    // if(!ms.EndDate.HasValue || ms.EndDate > next.StartDate) 
    if(ms.EndDate ?? DateTime.MaxValue >= next.StartDate) 
     return false; 
    } 
    lst.Insert(idx, ms); 
    return true; 
} 

Le suddette dichiarazioni false se fosse in grado di aggiungere alla lista. Se fosse più appropriato lanciare un'eccezione, questa è una modifica semplice.

0
public bool DoesAnOfferAlreadyExistWithinTheTimeframeProvided(int RetailerId, DateTime ValidFrom, DateTime ValidTo) 
     { 
      bool result = true; 

      try 
      { 
       // Obtain the current list of coupons associated to the retailer. 
       List<RetailerCoupon> retailerCoupons = PayPalInStore.Data.RetailerCoupon.Find(x => x.RetailerId == RetailerId).ToList(); 

       // Loop through each coupon and see if the timeframe provided in the NEW coupon doesnt fall between any EZISTING coupon. 
       if (retailerCoupons != null) 
       { 
        foreach (RetailerCoupon coupon in retailerCoupons) 
        { 
         DateTime retailerCouponValidFrom = coupon.DateValidFrom; 
         DateTime retailerCouponValidTo = coupon.DateExpires; 

         if ((ValidFrom <= retailerCouponValidFrom && ValidTo <= retailerCouponValidFrom) || (ValidFrom >= retailerCouponValidTo && ValidTo >= retailerCouponValidTo)) 
         { 
          return false; 
         } 
        } 
       } 

       return result; 
      } 
     catch (Exception ex) 
     { 
      this.errorManager.LogError("DoesAnOfferAlreadyExistWithinTheTimeframeProvided failed", ex); 
      return result; 
     } 
    } 
1

Un po 'in ritardo, ma non ho trovato questo modello in nessuna delle risposte/commenti.

if (startDate1 <= endDate2 && startDate2 <= endDate1) 
    { 
    // Overlaps. 
    } 
+0

[ed ecco un esempio] (https://dotnetfiddle.net/aQuvE3) – crosstalk

Problemi correlati