检查日期范围是否在日期范围内

我有以下课程:

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

我需要确保在添加到以下列表时新项目与现有项目的日期不重叠:

 var membership = new List { 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); 

我认为这很简单,但到目前为止我的尝试都是错误的,我开始迷惑自己,并希望有人做了类似的事情。 谢谢

基本上,如果日期范围的任何结尾都在另一个范围内,则日期范围会与另一个重叠,反之亦然。

 static bool AllowedToAdd(List 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); 

因此,如果我理解正确 – 您想确保日期范围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)) 

这是使用Collection的解决方案(缺少null参数validation,以及在Membership中validationEndDate > 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 { 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); } } 

像这样的条件应该做的伎俩:

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

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

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

如果您没有不同的排序标准,请首先按顺序维护列表。 由于不允许先前添加的对象重叠,因此一旦您知道要添加新对象的点,您只需比较任一侧的单个对象以确保允许新对象。 您还需要考虑“较早”对象的结束日期是否与“稍后”对象的开始日期重叠,因为此排序使得重叠的另一种可能性无关紧要。

因此,除了简化检测重叠的问题之外,我们可以将复杂度从O(n)降低到O(log n),而不是与所有现有项目进行比较,我们通过比较我们发现的0-2 O(log n)搜索。

 private class MembershipComparer : IComparer { public int Compare(Membership x, Membership y) { return x.StartDate.CompareTo(y.StartDate); } } private static bool AddMembership(List 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 。 如果抛出exception更合适,这是一个简单的修改。

 public bool DoesAnOfferAlreadyExistWithinTheTimeframeProvided(int RetailerId, DateTime ValidFrom, DateTime ValidTo) { bool result = true; try { // Obtain the current list of coupons associated to the retailer. List 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; } }