如何判断多时间范围重叠?求Objective-C/Swift多人可用时间交集函数
Swift实现:找出所有人员共同空闲的时间段
我来给你提供一个Swift的实现方案,用来计算所有指定人员都有空的时间段。这个方案既支持输入每个人的空闲时间段(直接求交集),也支持输入每个人的忙碌时间段(先转换为空闲再求交集)。
第一步:定义时间区间结构体
首先我们需要一个TimeRange结构体来表示时间区间,同时提供从字符串初始化的便捷方法:
import Foundation struct TimeRange { let start: Date let end: Date // 基础初始化器,确保开始时间早于结束时间 init?(start: Date, end: Date) { guard start < end else { return nil } self.start = start self.end = end } // 从字符串初始化,默认使用"yyyy-MM-dd HH:mm"格式 init?(startString: String, endString: String, dateFormat: String = "yyyy-MM-dd HH:mm") { let formatter = DateFormatter() formatter.dateFormat = dateFormat guard let start = formatter.date(from: startString), let end = formatter.date(from: endString), start < end else { return nil } self.start = start self.end = end } }
第二步:扩展时间区间的交集计算
为TimeRange添加扩展,用来计算两个时间区间的交集:
extension TimeRange { // 计算与另一个区间的交集,无交集则返回nil func intersection(with other: TimeRange) -> TimeRange? { let intersectStart = max(start, other.start) let intersectEnd = min(end, other.end) return intersectStart < intersectEnd ? TimeRange(start: intersectStart, end: intersectEnd) : nil } }
第三步:核心函数——求所有人的共同空闲时间段
这个函数接收每个人的空闲时间段数组,返回所有人员都有空的时间段:
func findCommonFreeTimeRanges(for peopleFreeRanges: [[TimeRange]]) -> [TimeRange] { guard !peopleFreeRanges.isEmpty else { return [] } // 初始候选集为第一个人的所有空闲区间 var commonRanges = peopleFreeRanges[0] // 遍历剩下的每个人的空闲区间 for personRanges in peopleFreeRanges.dropFirst() { var newCommonRanges: [TimeRange] = [] // 逐个计算当前候选区间与当前人员区间的交集 for commonRange in commonRanges { for personRange in personRanges { if let intersection = commonRange.intersection(with: personRange) { newCommonRanges.append(intersection) } } } // 如果中途没有交集了,直接返回空数组 guard !newCommonRanges.isEmpty else { return [] } commonRanges = newCommonRanges } // 对结果按开始时间排序 return commonRanges.sorted { $0.start < $1.start } }
第四步:测试你的示例
用你提供的示例数据来验证:
// 构建三个人的空闲时间段 guard let p1 = TimeRange(startString: "2016-01-01 12:00", endString: "2016-05-01 03:00"), let p2 = TimeRange(startString: "2016-01-01 03:00", endString: "2016-05-01 03:00"), let p3 = TimeRange(startString: "2016-01-01 03:00", endString: "2016-04-30 13:31") else { fatalError("无效的时间区间格式") } let peopleFreeRanges = [[p1], [p2], [p3]] let commonRanges = findCommonFreeTimeRanges(for: peopleFreeRanges) // 格式化输出结果 let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm" for range in commonRanges { print("start: \(dateFormatter.string(from: range.start)), end: \(dateFormatter.string(from: range.end))") }
运行这段代码会输出:
start: 2016-04-30 12:00, end: 2016-04-30 13:31
完全符合你的预期结果。
额外扩展:支持从忙碌时间段计算
如果你的输入是每个人的忙碌时间段,我们可以先把忙碌时间段转换为空闲时间段,再计算共同空闲区间:
extension Array where Element == TimeRange { // 合并重叠或相邻的时间区间 func merged() -> [TimeRange] { guard !isEmpty else { return [] } let sortedRanges = sorted { $0.start < $1.start } var mergedRanges = [sortedRanges[0]] for current in sortedRanges.dropFirst() { guard let last = mergedRanges.last else { continue } if current.start <= last.end { // 合并区间 let newRange = TimeRange(start: last.start, end: max(last.end, current.end))! mergedRanges.removeLast() mergedRanges.append(newRange) } else { mergedRanges.append(current) } } return mergedRanges } // 计算区间的补集(即空闲时间段),需要指定一个总时间范围 func complement(in totalRange: TimeRange) -> [TimeRange] { let mergedBusy = self.merged() var freeRanges: [TimeRange] = [] var currentStart = totalRange.start for busy in mergedBusy { if currentStart < busy.start { freeRanges.append(TimeRange(start: currentStart, end: busy.start)!) } currentStart = busy.end } if currentStart < totalRange.end { freeRanges.append(TimeRange(start: currentStart, end: totalRange.end)!) } return freeRanges } } // 从忙碌时间段计算共同空闲的函数 func findCommonFreeTimeRangesFromBusyRanges(for peopleBusyRanges: [[TimeRange]], totalTimeRange: TimeRange) -> [TimeRange] { // 把每个人的忙碌时间段转成空闲时间段 let peopleFreeRanges = peopleBusyRanges.map { $0.complement(in: totalTimeRange) } return findCommonFreeTimeRanges(for: peopleFreeRanges) }
比如,如果你的示例数据是忙碌时间段,你可以这样调用:
// 假设总时间范围是从2016-01-01 00:00到2016-05-01 04:00 guard let totalRange = TimeRange(startString: "2016-01-01 00:00", endString: "2016-05-01 04:00"), let p1Busy = TimeRange(startString: "2016-01-01 12:00", endString: "2016-05-01 03:00"), let p2Busy = TimeRange(startString: "2016-01-01 03:00", endString: "2016-05-01 03:00"), let p3Busy = TimeRange(startString: "2016-01-01 03:00", endString: "2016-04-30 13:31") else { fatalError("无效的时间区间") } let peopleBusyRanges = [[p1Busy], [p2Busy], [p3Busy]] let commonFreeRanges = findCommonFreeTimeRangesFromBusyRanges(for: peopleBusyRanges, totalTimeRange: totalRange) // 输出结果 for range in commonFreeRanges { print("start: \(dateFormatter.string(from: range.start)), end: \(dateFormatter.string(from: range.end))") }
这样就能得到同样的结果啦。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

