判断两个日期范围是否重叠

判断两个日期范围是否重叠

技术背景

在软件开发中,经常会遇到需要判断两个日期范围是否重叠的场景,例如会议安排、资源预约等。准确判断日期范围的重叠情况对于避免冲突、合理分配资源至关重要。

实现步骤

基本思路

判断两个日期范围 AB 是否重叠,可以通过分析它们的起始日期和结束日期之间的关系来实现。具体来说,需要考虑以下几种情况:

  1. 日期范围 A 完全在日期范围 B 之后。
  2. 日期范围 A 完全在日期范围 B 之前。
  3. 日期范围 AB 存在重叠。

具体步骤

  1. 定义日期范围:每个日期范围包含一个起始日期和一个结束日期。
  2. 判断条件:如果一个日期范围既不完全在另一个日期范围之后,也不完全在另一个日期范围之前,那么它们必然重叠。

核心代码

Python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class InclusiveRange:
"""InclusiveRange class to represent a lower and upper bound."""

def __init__(self, start, end):
"""Initialisation, ensures start <= end.
Args:
start: The start of the range.
end: The end of the range.
"""
self.start = min(start, end)
self.end = max(start, end)

def __repr__(self):
"""Return representation for f-string."""
return f"({self.start}, {self.end})"

def overlaps(self, other):
"""True if range overlaps with another.
Args:
other: The other InclusiveRange to check against.
"""

# Very limited recursion to ensure start of first range
# isn't after start of second.

if self.start > other.start:
return other.overlaps(self)

# Greatly simplified check for overlap.

return other.start <= self.end


def test_case(range1, range2):
"""Single test case checker."""

# Get low and high value for "graphic" output.

low = min(range1.start, range2.start)
high = max(range1.end, range2.end)

# Output ranges and graphic.

print(f"r1={range1} r2={range2}: ", end="")
for val in range(low, high + 1):
is_in_first = range1.start <= val <= range1.end
is_in_second = range2.start <= val <= range2.end

if is_in_first and is_in_second:
print("|", end="")
elif is_in_first:
print("'", end="")
elif is_in_second:
print(",", end="")
else:
print(" ", end="")

# Finally, output result of overlap check.

print(f" - {range1.overlaps(range2)}\n")


# Various test cases, add others if you doubt the correctness.

test_case(InclusiveRange(0, 1), InclusiveRange(8, 9))
test_case(InclusiveRange(0, 4), InclusiveRange(5, 9))
test_case(InclusiveRange(0, 4), InclusiveRange(4, 9))
test_case(InclusiveRange(0, 7), InclusiveRange(2, 9))
test_case(InclusiveRange(0, 4), InclusiveRange(0, 9))
test_case(InclusiveRange(0, 9), InclusiveRange(0, 9))
test_case(InclusiveRange(0, 9), InclusiveRange(4, 5))

test_case(InclusiveRange(8, 9), InclusiveRange(0, 1))
test_case(InclusiveRange(5, 9), InclusiveRange(0, 4))
test_case(InclusiveRange(4, 9), InclusiveRange(0, 4))
test_case(InclusiveRange(2, 9), InclusiveRange(0, 7))
test_case(InclusiveRange(0, 9), InclusiveRange(0, 4))
test_case(InclusiveRange(0, 9), InclusiveRange(0, 9))
test_case(InclusiveRange(4, 5), InclusiveRange(0, 9))

JavaScript 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* Compares to comparable objects to find out whether they overlap.
* It is assumed that the interval is in the format [from,to) (read: from is inclusive, to is exclusive).
* A null value is interpreted as infinity
*/
function intervalsOverlap(from1, to1, from2, to2) {
return (to2 === null || from1 < to2) && (to1 === null || to1 > from2);
}

describe('', function () {
function generateTest(firstRange, secondRange, expected) {
it(JSON.stringify(firstRange) + ' and ' + JSON.stringify(secondRange), function () {
expect(intervalsOverlap(firstRange[0], firstRange[1], secondRange[0], secondRange[1])).toBe(expected);
});
}

describe('no overlap (touching ends)', function () {
generateTest([10, 20], [20, 30], false);
generateTest([20, 30], [10, 20], false);

generateTest([10, 20], [20, null], false);
generateTest([20, null], [10, 20], false);

generateTest([null, 20], [20, 30], false);
generateTest([20, 30], [null, 20], false);
});

describe('do overlap (one end overlaps)', function () {
generateTest([10, 20], [19, 30], true);
generateTest([19, 30], [10, 20], true);

generateTest([10, 20], [null, 30], true);
generateTest([10, 20], [19, null], true);
generateTest([null, 30], [10, 20], true);
generateTest([19, null], [10, 20], true);
});

describe('do overlap (one range included in other range)', function () {
generateTest([10, 40], [20, 30], true);
generateTest([20, 30], [10, 40], true);

generateTest([10, 40], [null, null], true);
generateTest([null, null], [10, 40], true);
});

describe('do overlap (both ranges equal)', function () {
generateTest([10, 20], [10, 20], true);

generateTest([null, 20], [null, 20], true);
generateTest([10, null], [10, null], true);
generateTest([null, null], [null, null], true);
});
});

Java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.sql.Timestamp;

public class DateRangeOverlap {
private static Boolean overlap(Timestamp startA, Timestamp endA,
Timestamp startB, Timestamp endB) {
return (endB == null || startA == null || !startA.after(endB))
&& (endA == null || startB == null || !endA.before(startB));
}

public static void main(String[] args) {
// 示例代码,可根据实际情况修改
Timestamp startA = Timestamp.valueOf("2023-01-01 00:00:00");
Timestamp endA = Timestamp.valueOf("2023-01-10 00:00:00");
Timestamp startB = Timestamp.valueOf("2023-01-05 00:00:00");
Timestamp endB = Timestamp.valueOf("2023-01-15 00:00:00");

Boolean isOverlap = overlap(startA, endA, startB, endB);
System.out.println("是否重叠: " + isOverlap);
}
}

最佳实践

  • 考虑区间类型:在判断日期范围重叠时,需要明确区间是开区间、闭区间还是半开半闭区间,不同的区间类型可能会影响判断结果。
  • 处理空值:如果日期范围的起始日期或结束日期可能为空,需要在代码中进行相应的处理,避免出现空指针异常。
  • 使用成熟的日期库:在实际开发中,建议使用成熟的日期处理库,如 Python 的 datetime 模块、Java 的 java.time 框架、JavaScript 的 moment.jsdate-fns 等,这些库提供了丰富的日期处理功能,可以简化开发过程。

常见问题

区间类型问题

不同的区间类型(开区间、闭区间、半开半闭区间)可能会导致判断结果不同。例如,对于闭区间 [StartA, EndA][StartB, EndB],重叠条件为 (StartA <= EndB) and (EndA >= StartB);而对于开区间 (StartA, EndA)(StartB, EndB),重叠条件为 (StartA < EndB) and (EndA > StartB)

空区间问题

空区间是指起始日期和结束日期相同的区间,例如 [StartA, StartA]。在判断日期范围重叠时,需要特殊处理空区间,因为空区间与其他区间的重叠情况可能不符合常规的判断条件。

日期格式问题

在处理日期时,需要确保日期格式的一致性。如果日期格式不一致,可能会导致比较结果错误。建议在比较日期之前,将日期转换为统一的格式。


判断两个日期范围是否重叠
https://119291.xyz/posts/determine-whether-two-date-ranges-overlap/
作者
ww
发布于
2025年5月28日
许可协议