检查的日期范围的日期范围内

检查的日期范围的日期范围内

问题描述:

我有以下类:检查的日期范围的日期范围内

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

我想这将是简单的,但到目前为止,我的尝试已经全部WRO恩,我开始迷惑自己,并希望有人做了类似的事情,他们可以分享。由于

基本上,日期范围重叠的另一如果任何其结局是在另一个范围内,反之亦然。

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 2010-09-24 17:53:54

像这样的情况应该做的伎俩:

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

这是非常符合逻辑和天才的解决方案。 Thnx @Joachim VR – 2017-05-06 08:23:39

所以,如果我理解正确的这 - 你要确保日期范围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 2010-09-24 12:12:47

下面是使用Collection<T>溶液(缺少null参数验证和确认中MembershipEndDate > StartDate):

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

如果你不这样做有不同的排序标准,然后开始按顺序维护您的列表。由于之前添加的对象不允许重叠,因此一旦知道添加新对象的位置,您只需比较任一侧的单个对象以确保允许新对象。您还需要考虑“早期”对象的结束日期是否与“稍后”对象的开始日期重叠,因为此排序使得重叠的其他可能性无关。因此,除了简化检测重叠的问题,我们可以将复杂度从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。如果抛出异常更合适,这是一个简单的修改。

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

有点晚了,但我在答案/评论的任何地方都找不到这种模式。

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

[这里是一个例子](https://dotnetfiddle.net/aQuvE3) – crosstalk 2016-07-13 07:52:11