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

时间:2010-09-24 12:00:34

标签: c#

我有以下课程:

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

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

8 个答案:

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