I want to exclude Array of DateInterval
from Array of DateInterval
. This's my code, but I don't think it will not be helpful.. it goes in infinite loop sometimes and I couldn't solve it.
extension DateInterval {
static func exclude(_ excludedIntervals: [DateInterval], from periods: [DateInterval]) -> [DateInterval] {
if excludedIntervals.isEmpty { return periods }
var resultSlots: [DateInterval] = []
for period in periods {
let results = period.exclude(excludedIntervals)
resultSlots.append(contentsOf: results)
}
return resultSlots
}
func exclude(_ execludedIntervals: [DateInterval]) -> [DateInterval] {
if execludedIntervals.isEmpty { return [self] }
var sortedExecludedIntervals = execludedIntervals.sorted()
var resultSlots: [DateInterval] = []
var execludedInterval = sortedExecludedIntervals.removeFirst()
// remove execludedIntervals from self
if let intersection = self.intersection(with: execludedInterval) {
if self.start == intersection.start && self.end > intersection.end {
let newSlot = DateInterval(start: intersection.end, end: self.end)
resultSlots.append(contentsOf: newSlot.exclude(sortedExecludedIntervals))
} else if self.start < intersection.start && self.end == intersection.end {
let newSlot = DateInterval(start: self.start, end: intersection.start)
resultSlots.append(contentsOf: newSlot.exclude(sortedExecludedIntervals))
} else if self.start < intersection.start && self.end > intersection.end {
let preSlot = DateInterval(start: self.start, end: intersection.start)
resultSlots.append(contentsOf: preSlot.exclude(sortedExecludedIntervals))
let postSlot = DateInterval(start: intersection.end, end: self.end)
resultSlots.append(contentsOf: postSlot.exclude(sortedExecludedIntervals))
} else {
// start = start && end = end
resultSlots = []
return resultSlots
}
}
return resultSlots
}
}
For example, I want to exclude 1 pm- 3pm and 5 pm - 6 pm intervals from an interval 12 pm - 6pm. The function should return 12 pm - 1 pm and 3 pm - 5 pm.
A couple of thoughts:
If I want a method to operate on an array of DateInterval
, I’d suggest putting it in an Array
(or Sequence
) extension, constrained to DateInterval
types, rather than a static
method on DateInterval
:
extension Array where Element == DateInterval {
func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] { ... }
}
When considering excluding a DateInterval
from another, there are tons of different scenarios:
In my mind, it gets too messy to think of all of those scenarios, so I decided to simplify this a bit and decide:
DateInterval
supplies a nice method to do that for us);before
interval and an after
interval (e.g. if I cut 2pm-3pm out of noon-6pm, the before
interval will be noon-2pm and the after
interval will be 3pm-6pm);while
statement, manually checking and adjusting the current index
.That yields:
extension Array where Element == DateInterval {
func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] {
var results: [DateInterval] = self
for excludedInterval in excludedIntervals {
var index = results.startIndex
while index < results.endIndex {
let interval = results[index]
if let intersection = interval.intersection(with: excludedInterval) {
var before: DateInterval?
var after: DateInterval?
if intersection.start > interval.start {
before = DateInterval(start: interval.start, end: intersection.start)
}
if intersection.end < interval.end {
after = DateInterval(start: intersection.end, end: interval.end)
}
let replacements = [before, after].compactMap { $0 }
results.replaceSubrange(index...index, with: replacements)
index += replacements.count
} else {
index += 1
}
}
}
return results
}
}
Then we can consider the exclusion applied to a single DateInterval
as just a special case of an array with one item:
extension DateInterval {
func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] {
return [self].exclude(excludedIntervals)
}
}
So:
let formatter = ISO8601DateFormatter()
let start = formatter.date(from: "2019-02-09T12:00:00Z")!
let end = formatter.date(from: "2019-02-09T18:00:00Z")!
let exclude1Start = formatter.date(from: "2019-02-09T13:00:00Z")!
let exclude1End = formatter.date(from: "2019-02-09T14:00:00Z")!
let exclude2Start = formatter.date(from: "2019-02-09T16:00:00Z")!
let exclude2End = formatter.date(from: "2019-02-09T17:00:00Z")!
let intervals = DateInterval(start: start, end: end)
.exclude([
DateInterval(start: exclude1Start, end: exclude1End),
DateInterval(start: exclude2Start, end: exclude2End)
])
print(intervals)
Will produce:
[
2019-02-09 12:00:00 +0000 to 2019-02-09 13:00:00 +0000,
2019-02-09 14:00:00 +0000 to 2019-02-09 16:00:00 +0000,
2019-02-09 17:00:00 +0000 to 2019-02-09 18:00:00 +0000
]