2010-09-24 4 views
9

У меня есть следующий класс:Проверьте диапазон дат находится в пределах диапазона дат

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

Мне нужно, чтобы убедиться, что при добавлении в следующем список, что новый элемент не перекрывает дату из существующего пункта :

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

Например делать:

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

Я думал, что это было бы просто, но до сих пор мои попытки были все сонате и я начинаю путать себя и надеялся, что кто-то сделал что-то подобное, что они могли бы поделиться. Благодаря

ответ

14

В основном, диапазон дат перекрывает другой, если какой-либо из его окончаний в другом диапазоне или наоборот.

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

С использованием:

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

Спасибо за ответы всем, но я предпочитаю этот, так как это легче всего понять. – nfplee

1

Условия, как это следует сделать трюк:

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

Это очень логично и гениальное решение. Thnx @Joachim VR –

5

Так что, если я понимаю, это правильно - вы хотите, чтобы убедиться, что диапазон дат 2 не находится в пределах диапазона дат 1?

Например:

startDate1 = 01/01/2011 

endDate1 = 01/02/2011 

и

startDate2 = 19/01/2011 

endDate2 = 10/02/2011 

Это должно быть простой случай:

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

вы также должны проверить нулевое значение. – jimplode

2

Вот решение (отсутствует null аргумент-проверки и проверки в Membership, что EndDate > StartDate) с помощью 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

Если вы не имеют разные критерии сортировки, а затем начинайте с упорядочивания списка. Поскольку ранее не добавляемый объект не может перекрываться, то, как только вы узнаете, в какой точке добавить новый объект, вам нужно сравнить только отдельные объекты с каждой стороны, чтобы убедиться, что новый объект разрешен. Вам также нужно только учитывать, совпадает ли дата окончания «более раннего» объекта с датой начала «более позднего» объекта, так как это упорядочение делает другую возможность перекрытия нерелевантной.

Следовательно, а также упрощая вопрос об обнаружении перекрытий, мы можем уменьшить сложность от O (n) до O (log n), вместо сравнения со всеми существующими элементами, мы сравниваем с 0-2, найденный через поиск 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; 
} 

Вышеприведенные возвращается false, если не удалось добавить в список. Если было бы более целесообразно выкинуть исключение, это будет легкой модификацией.

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

Немного поздно, но я не мог найти этот шаблон в любом месте в ответах/комментариях.

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

[И вот пример] (https://dotnetfiddle.net/aQuvE3) – crosstalk

Смежные вопросы