我有以下课程:
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);
我认为这很简单,但到目前为止我的尝试都是错误的,我开始迷惑自己,并希望有人做了类似的事情。感谢
答案 0 :(得分:15)
基本上,如果日期范围的任何结尾都在另一个范围内,则日期范围会重叠,反之亦然。
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);
答案 1 :(得分: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))
答案 2 :(得分: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);
}
}
答案 3 :(得分:1)
这样的条件应该可以解决问题:
newItem.StartDate <= range.EndDate && newItem.EndDate.HasValue && newItem.EndDate >= range.StartDate
答案 4 :(得分:1)
有点晚了但我在答案/评论的任何地方都找不到这种模式。
if (startDate1 <= endDate2 && startDate2 <= endDate1)
{
// Overlaps.
}
答案 5 :(得分: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
。如果抛出异常更合适,这是一个很容易的修改。
答案 6 :(得分: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;
}
}
答案 7 :(得分:0)
我想出了以下方法来检查日期是否重叠,这可能不是最有效的方法,但是我希望这会有所帮助。
public static bool DateRangesOverlap(DateTime startDateA, DateTime endDateA, DateTime startDateB, DateTime endDateB)
{
var allDatesA = new List<DateTime>();
var allDatesB = new List<DateTime>();
for (DateTime date = startDateA; date <= endDateA; date = date.AddDays(1))
{
allDatesA.Add(date);
}
for (DateTime date = startDateB; date <= endDateB; date = date.AddDays(1))
{
allDatesB.Add(date);
}
var isInRange = false;
foreach (var date in allDatesA)
{
var existsInAllDatesB = allDatesB.Any(x => x == date);
if (existsInAllDatesB)
{
isInRange = true;
break;
}
}
return isInRange;
}