Пара мыслей:
Если я хочу, чтобы метод работал с массивом DateInterval
, я бы предложил поместить его в Array
(или Sequence
) расширение, ограниченное DateInterval
типами, а не static
метод для DateInterval
:
extension Array where Element == DateInterval {
func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] { ... }
}
При рассмотрении вопроса об исключении DateInterval
из другого, существуюттонн различных сценариев:
- Вы можете исключить небольшое окно из середины интервала;
- Вы можете исключить часть в начале интервала;
- Вы можете исключить часть в конце интервала;и
- Вы можете исключить весь интервал.
На мой взгляд, становится слишком грязно, чтобы думать обо всех этих сценариях, поэтому я решил немного упростить это и решить:
- Где именно исключаетсяобласть пересекается с текущим интервалом (и
DateInterval
предоставляет хороший способ сделать это для нас); - Если я «вырежу» это пересечение из интервала дат, я мог бы получить два интервала, aИнтервал
before
и интервал after
(например, если я вырежу 14:00 - 15:00 из полудня - 6:00, интервал before
будет равняться 12:00 - 14:00, а интервал after
- 15:00 - 18:00); - Затем алгоритм переходит вниз: «Если интервал пересекается исключенной областью, замените исходный интервал двумя другими интервалами: один до и один после»;и
- Учитывая, что я изменяю исходный массив результирующих интервалов, я бы предложил вложенные циклы, причем внешний цикл - это интервалы, которые должны быть исключены, а внутренний цикл - результирующие интервалы, которые, поскольку он мутируетЯ проведу итерацию с помощью оператора
while
, проверяя и корректируя текущий index
вручную.
То есть:
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
}
}
Тогда мы можем рассматривать исключение, примененное к одному DateInterval
, как особый случай массива с однимпункт:
extension DateInterval {
func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] {
return [self].exclude(excludedIntervals)
}
}
Итак:
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)
Произведет:
[
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
]