检查日期范围是否在日期范围内
我有以下课程:
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; } }
- 在ProtoBuf-net中序列化字典失败
- 扩展ASP.NET MVC 2 Model Binder以适用于0,1个布尔值
- 在.NET远程处理RemotingConfiguration.RegisterWellKnownServiceType和RemotingServices.Marshal之间有什么区别?
- 用于Excel克隆的正确数据结构
- 如何自定义日期时间格式或将DateTime转换为具有所需格式的String
- 如何在.NET类库中创建“抽象”枚举?
- 如何使用数字字符实体而不是问号XmlDocument.Save()到encoding =“us-ascii”?
- 我该怎么用Sleep或Timer
- ASP.NET:如何创建可扩展的空TreeNode