2010-09-24 23 views
9

tengo la clase siguiente:Comprobar si un intervalo de fechas está dentro de un intervalo de fechas

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

necesito para asegurarse de que cuando se añade a la lista después de que el nuevo elemento no se solapa con las fechas de elemento existente :

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

Por ejemplo haciendo:

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

pensé que esto sería sencillo pero hasta ahora mis intentos han sido WRO ng y estoy empezando a confundirme y esperaba que alguien hubiera hecho algo similar que pudieran compartir. Gracias

Respuesta

14

Básicamente, un intervalo de fechas se superpone a otra si cualquiera de sus terminaciones son dentro del otro rango, 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 el uso:

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

Gracias por las respuestas de todos, pero yo prefiero este, ya que es el más fácil de entender lol. – nfplee

1

una condición como esta debe hacer el truco:

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

Esto es muy lógico y una solución genial. Thnx @Joachim VR –

5

Así que si lo entiendo correctamente - que quiere asegurarse de que el intervalo de fechas 2 no está comprendida en el periodo 1?

Por ejemplo:

startDate1 = 01/01/2011 

endDate1 = 01/02/2011 

y

startDate2 = 19/01/2011 

endDate2 = 10/02/2011 

esto debería ser un simple caso de:

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

también debe verificar el valor nulo. – jimplode

2

he aquí una solución (falta null argumento de validación y validación en Membership que EndDate > StartDate) usando 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

Si no lo hace tiene diferentes criterios para clasificar, luego comience por mantener su lista en orden. Como ningún objeto previamente agregado puede superponerse, una vez que conozca el punto donde agregaría un nuevo objeto, solo necesita comparar los objetos individuales en cada lado para asegurarse de que el nuevo objeto esté permitido. También solo necesita considerar si la fecha de finalización del objeto "anterior" se superpone con la fecha de inicio del objeto "posterior", ya que este orden hace que la otra posibilidad para una superposición sea irrelevante.

Por lo tanto, además de simplificar la cuestión de la detección de superposiciones, podemos reducir la complejidad de O (n) a O (log n), en lugar de comparar con todos los elementos existentes, comparamos con 0-2 ' se encuentra a través de una búsqueda 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; 
} 

Las anteriores declaraciones de false si era incapaz de añadir a la lista. Si sería más apropiado lanzar una excepción, esta es una modificación fácil.

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 poco tarde, pero no pude encontrar este patrón en cualquier parte de las respuestas/comentarios.

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

[Y aquí hay un ejemplo] (https://dotnetfiddle.net/aQuvE3) – crosstalk

Cuestiones relacionadas