You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
5.7 KiB

//
// TimePeriodChain.swift
// DateTools
//
// Created by Grayson Webster on 8/17/16.
// Copyright © 2016 Grayson Webster. All rights reserved.
//
import Foundation
/**
* Time period chains serve as a tightly coupled set of time periods. They are
* always organized by start and end date, and have their own characteristics like
6 years ago
* a StartDate and EndDate that are extrapolated from the time periods within. Time
* period chains do not allow overlaps within their set of time periods. This type of
* group is ideal for modeling schedules like sequential meetings or appointments.
*
* [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-chains) for more information.
*/
open class TimePeriodChain: TimePeriodGroup {
// MARK: - Chain Existence Manipulation
6 years ago
/**
* Append a TimePeriodProtocol to the periods array and update the Chain's
* beginning and end.
*
* - parameter period: TimePeriodProtocol to add to the collection
*/
public func append(_ period: TimePeriodProtocol) {
6 years ago
let beginning = (periods.isEmpty == false) ? periods.last!.end! : period.beginning
6 years ago
let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration)
6 years ago
periods.append(newPeriod)
6 years ago
6 years ago
// Update updateExtremes
if periods.count == 1 {
_beginning = period.beginning
_end = period.end
6 years ago
} else {
_end = _end?.addingTimeInterval(period.duration)
}
}
6 years ago
/**
* Append a TimePeriodProtocol array to the periods array and update the Chain's
* beginning and end.
*
* - parameter periodArray: TimePeriodProtocol list to add to the collection
*/
public func append<G: TimePeriodGroup>(contentsOf group: G) {
for period in group.periods {
6 years ago
let beginning = (periods.isEmpty == false) ? periods.last!.end! : period.beginning
6 years ago
let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration)
6 years ago
periods.append(newPeriod)
6 years ago
6 years ago
// Update updateExtremes
if periods.count == 1 {
_beginning = period.beginning
_end = period.end
6 years ago
} else {
_end = _end?.addingTimeInterval(period.duration)
}
}
}
6 years ago
/**
* Insert period into periods array at given index.
*
* - parameter newElement: The period to insert
* - parameter index: Index to insert period at
*/
public func insert(_ period: TimePeriodProtocol, at index: Int) {
6 years ago
// Check for special zero case which takes the beginning date
if index == 0, period.beginning != nil, period.end != nil {
// Insert new period
periods.insert(period, at: index)
6 years ago
} else if period.beginning != nil, period.end != nil {
// Insert new period
periods.insert(period, at: index)
6 years ago
} else {
Logger.info("All TimePeriods in a TimePeriodChain must contain a defined start and end date")
return
}
6 years ago
6 years ago
// Shift all periods after inserted period
for i in 0 ..< periods.count {
if i > index, i > 0 {
let currentPeriod = TimePeriod(beginning: period.beginning, end: period.end)
6 years ago
periods[i].beginning = periods[i - 1].end
periods[i].end = periods[i].beginning!.addingTimeInterval(currentPeriod.duration)
}
}
6 years ago
updateExtremes()
}
6 years ago
/**
* Remove from period array at the given index.
*
* - parameter at: The index in the collection to remove
*/
public func remove(at index: Int) {
6 years ago
// Retrieve duration of period to be removed
let duration = periods[index].duration
6 years ago
6 years ago
// Remove period
periods.remove(at: index)
6 years ago
6 years ago
// Shift all periods after inserted period
for i in index ..< periods.count {
periods[i].shift(by: -duration)
}
updateExtremes()
}
6 years ago
/**
* Remove all periods from period array.
*/
public func removeAll() {
6 years ago
periods.removeAll()
updateExtremes()
}
6 years ago
// MARK: - Chain Content Manipulation
/**
* In place, shifts all chain time periods by a given time interval
*
* - parameter duration: The time interval to shift the period by
*/
public func shift(by duration: TimeInterval) {
6 years ago
for var period in periods {
period.shift(by: duration)
}
6 years ago
_beginning = _beginning?.addingTimeInterval(duration)
_end = _end?.addingTimeInterval(duration)
}
6 years ago
public override func map<T>(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] {
return try periods.map(transform)
}
6 years ago
public override func filter(_ isIncluded: (TimePeriodProtocol) throws -> Bool) rethrows -> [TimePeriodProtocol] {
return try periods.filter(isIncluded)
}
6 years ago
internal override func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result {
return try periods.reduce(initialResult, nextPartialResult)
}
6 years ago
/**
* Removes the last object from the `TimePeriodChain` and returns it
*
*/
public func pop() -> TimePeriodProtocol? {
6 years ago
let period = periods.popLast()
updateExtremes()
6 years ago
return period
}
6 years ago
internal func updateExtremes() {
_beginning = periods.first?.beginning
_end = periods.last?.end
}
6 years ago
// MARK: - Operator Overloads
6 years ago
/**
* Operator overload for comparing `TimePeriodChain`s to each other
*/
6 years ago
public static func == (left: TimePeriodChain, right: TimePeriodChain) -> Bool {
return left.equals(right)
}
}