日期范围重叠与可空日期

5
我正在寻找对这里提出的问题的详细答案:确定两个日期范围是否重叠,其中任何一个日期范围中的日期都可以为null。我想到了以下解决方案,但不确定是否可以进一步简化。请注意保留HTML标记。
(StartA == NULL || StartA <= EndB) &&
(EndA == NULL || EndA >= StartB) &&
(StartB == NULL || StartB <= EndA) &&
(EndB == NULL || EndB >= StartA)

假设:
开始时间为StartA到结束时间为EndA,开始时间为StartB到结束时间为EndB。
编辑:抱歉我匆忙地组织了上述逻辑,当任一范围的开始和结束日期为空时,似乎会失败。请参见David下面的解决方案,以获得更好的并且解释清楚的方法。

如果StartA为空,您将会遇到错误,因为比较运算符无法与null一起使用。将所有的or Gate{Logic}转换为and{Logic},然后重新组织语句,将所有比较为null的语句放在开头,这样短路and{Logic}就能正常工作了。 - Waleed A.K.
4个回答

14

这种情况可以通过稍微推广Charles Bretana的优秀回答来处理。

假设CondA表示日期范围A完全在日期范围B之后(如果StartA>EndB,则为真) 假设CondB表示日期范围A完全在日期范围B之前(如果EndA<StartB,则为真)

在这种情况下,假设您希望使用空日期表示“没有起始/结束限制”,则需要修改条件。例如,对于CondA,为了确保日期范围A完全在日期范围B之后,日期范围A必须具有定义的起始时间,日期范围B必须具有定义的结束时间,并且 A的起始时间必须晚于B的结束时间:

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

CondB与A和B交换后相同:

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

接着说,如果 A 和 B 都不为真,则存在重叠。

Overlap := !(CondA || CondB)

并且

现在deMorgan定律,我认为是这样说的

非(A或B) <=> 非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 == NULL但是StartA不为null,则你的第一个条件将会比较StartA <= NULL。在我熟悉的大多数编程语言中,这都是一种错误情况。


感谢你详细的解释,大卫!是啊,我发了帖子后很快意识到我的初始解决方案很快就会崩溃。 - Josh
不错的方法,就像德摩根定理一样。但是你不能将第二部分实现为编程语句,因为如果任何一个例子中的startA或EndB等于null,则比较运算符将失败。 尽量只使用And {&&}运算符,因为它在大多数新编译器中作为短路运算符工作。 这是正确的低级和编程方式。 ![(StartA != null) && (EndB != null) && (StartA > EndB)] && ![(StartB != null) && (EndA != null) && (StartB > EndA)] - Waleed A.K.
1
@Waleed:如果你说的是我的最后两行代码,那么它们肯定可以作为语句实现。如果startAendB等于null,那么这将被该条件的显式测试捕获,并且<=比较将永远不会进行。||也使用短路评估。(大多数编译器可能会使用德摩根定律进行优化,以便最后两行和前面两行生成相同的编译代码。) - David Z

1

如果不考虑空值,则答案为

(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从时间开始就存在。在这种情况下,DateRangeB永远不可能在DateRangeA之前。因此,第一个条件(StartA(BOT) <= EndB)将始终为真,无论EndB是什么。所以将这个表达式改为:当StartA为空时,将EndB与自身进行比较。无论EndB是什么,表达式EndB <= EndB都是正确的。(我们可以创建变量来表示BOT和EOT,但这样更容易)。

对于其他三个输入变量也要做同样的处理。


-1

这可能是你能够得到的“简单”版本,尽管我实际上还没有证明它。

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


-1

所有答案都基于条件为真的情况下。我想在这里添加一些注释。

1- DateTime 变量类型是一个结构体,你不能将其设置为 null,除非你使用可空类型,如 "DateTime?"。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接