具有可空日期的日期范围重叠

时间:2010-07-07 15:10:11

标签: language-agnostic datetime math

我正在寻找对此处提出的问题的扩展答案:

Determine Whether Two Date Ranges Overlap

其中任一日期范围中的任何日期都可以为null。我想出了以下解决方案,但我不确定它是否可以进一步简化。

(StartA == NULL || StartA <= EndB) &&
(EndA == NULL || EndA >= StartB) &&
(StartB == NULL || StartB <= EndA) &&
(EndB == NULL || EndB >= StartA)

假设:

StartA到EndA和StartB到EndB的DateTime范围

编辑:抱歉,我很快将上述逻辑放在一起,当范围的开始和结束日期都为NULL时,这似乎失败了。请参阅下面的David的解决方案以获得更好的&amp;解释得很好的方法。

4 个答案:

答案 0 :(得分:12)

这个案例可以通过Charles Bretana's excellent answer对该问题的略微概括来处理。

  

让CondA平均DateRange完全在DateRange B之后(如果StartA&gt; EndB则为真)   设CondB平均DateRange A完全在DateRange B之前(如果EndA&lt; StartB则为真)

在这种情况下,假设您希望空日期表示“无开始/结束边界”,则会修改条件。例如,对于CondA,为了使DateRange A完全在DateRange B之后,DateRange A必须具有已定义的开始时间,DateRange B必须具有已定义的结束时间, A的时间必须在B的结束时间之后:

CondA := (StartA != null) && (EndB != null) && (StartA > EndB)

CondB与A和B切换相同:

CondB := (StartB != null) && (EndA != null) && (StartB > EndA)

继续,

  

如果A Nor B都不成立则存在重叠

Overlap := !(CondA || CondB)

  

现在deMorgan的法律,我认为是,

     

不(A或B)&lt; =&gt;不是A而不是B

Overlap == !CondA && !CondB
        == ![(StartA != null) && (EndB != null) && (StartA > EndB)] &&
           ![(StartB != null) && (EndA != null) && (StartB > EndA)]
        == [(StartA == null) || (EndB == null) || (StartA <= EndB)] &&
           [(StartB == null) || (EndA == null) || (StartB <= EndA)]

我认为这实际上比您开发的解决方案更强大,因为如果EndB == NULLStartA不为空,那么您的第一个条件将会比较StartA <= NULL。在我熟悉的大多数语言中,这是一个错误条件。

答案 1 :(得分:0)

尽管我还没有真正证明这一点,但这可能是“简单”的。

进一步简化可能是不值得的,因为在最坏的情况下,该块最终会进行大约8次操作(由于短路评估,平均为4次)。

答案 2 :(得分:0)

如果条件为真,所有答案均以此为基础。我想在这里补充说明。

1- DateTime变量类型是一个结构,除非你使用像“DateTime?”这样的可空类型,否则你不能将它设置为null。

2-要查找重叠范围,请执行以下步骤

DateTime? StartOverLap = null,EndOverLap = null;
            if (StartA != null && StartB != null)
            {
                StartOverLap = StartA > StartB ? StartA : StartB;
            }
            else if (StartA == null && StartB != null)
            {
                StartOverLap = StartB;
            }
            else if (StartA != null && StartB == null)
            {
                StartOverLap = StartA;
            }
            if (EndA != null && EndB != null)
            {
                EndOverLap = EndA < EndB ? EndA : EndB;
            }
            else if (EndA == null && EndB != null)
            {
                EndOverLap = EndB;
            }
            else if (EndA != null && EndB == null)
            {
                EndOverLap = EndA;
            }
            if (StartOverLap != null && EndOverLap == null)
            {
                if (EndOverLap < StartOverLap)
                {
                    StartOverLap = null;
                    EndOverLap = null;
                }
            }

答案 3 :(得分:0)

不考虑空值,答案是

(StartA <= EndB) and (EndA >= StartB)   (详见this

考虑开始日期和结束日期的空值,
使用C三元运算符语法:
   (StartA != null? StartA: EndB <= EndB != null? EndB: StartA) && (EndA != null? EndA: StartB >= StartB != null? StartB: EndA)

或C#4.x样式空操作符:

(StartA??EndB <= EndB??StartA) && (EndA??StartB >= StartB??EndA)

或在SQL中:

(Coalesce(StartA, EndB) <= Coalesce(EndB, StartA)) And (Coalesce(EndA, StartB ) <= Coalesce(StartB , EndA))

说明:
考虑非空答案:
(StartA <= EndB) and (EndA >= StartB)

现在,考虑StartA为空,表示日期范围A自开始时间(BOT)起已存在。在这种情况下,DateRangeB永远不会出现在DateRangeA之前。因此,无论EndB是什么,第一个条件(StartA(BOT)&lt; = EndB)都将始终为真。因此,更改此表达式,以便在StartA为null时,将EndB与其自身进行比较,而不是将null与EndB进行比较 无论EndB是什么,表达式EndB <= EndB都将为真。 (我们可以创建表示BOT和EOT的变量,但这更容易)。

对其他三个输入变量执行相同操作。