// // SwiftDate // Parse, validate, manipulate, and display dates, time and timezones in Swift // // Created by Daniele Margutti // - Web: https://www.danielemargutti.com // - Twitter: https://twitter.com/danielemargutti // - Mail: hello@danielemargutti.com // // Copyright © 2019 Daniele Margutti. Licensed under MIT License. // 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 /// 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. open class TimePeriodChain: TimePeriodGroup { // MARK: - Chain Existence Manipulation /** * 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) { let beginning = (periods.count > 0) ? periods.last!.end! : period.start let newPeriod = TimePeriod(start: beginning!, duration: period.duration) periods.append(newPeriod) //Update updateExtremes if periods.count == 1 { start = period.start end = period.end } else { end = end?.addingTimeInterval(period.duration) } } /** * 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(contentsOf group: G) { for period in group.periods { let beginning = (periods.count > 0) ? periods.last!.end! : period.start let newPeriod = TimePeriod(start: beginning!, duration: period.duration) periods.append(newPeriod) //Update updateExtremes if periods.count == 1 { start = period.start end = period.end } else { end = end?.addingTimeInterval(period.duration) } } } /// Insert period into periods array at given index. /// /// - Parameters: /// - period: The period to insert /// - index: Index to insert period at public func insert(_ period: TimePeriodProtocol, at index: Int) { //Check for special zero case which takes the beginning date if index == 0 && period.start != nil && period.end != nil { //Insert new period periods.insert(period, at: index) } else if period.start != nil && period.end != nil { //Insert new period periods.insert(period, at: index) } else { print("All TimePeriods in a TimePeriodChain must contain a defined start and end date") return } //Shift all periods after inserted period for i in 0.. index && i > 0 { let currentPeriod = TimePeriod(start: period.start, end: period.end) periods[i].start = periods[i - 1].end periods[i].end = periods[i].start!.addingTimeInterval(currentPeriod.duration) } } updateExtremes() } /// Remove from period array at the given index. /// /// - Parameter index: The index in the collection to remove public func remove(at index: Int) { //Retrieve duration of period to be removed let duration = periods[index].duration //Remove period periods.remove(at: index) //Shift all periods after inserted period for i in index..(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] { return try periods.map(transform) } public override func filter(_ isIncluded: (TimePeriodProtocol) throws -> Bool) rethrows -> [TimePeriodProtocol] { return try periods.filter(isIncluded) } internal override func reduce(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result { return try periods.reduce(initialResult, nextPartialResult) } /// Removes the last object from the `TimePeriodChain` and returns it public func pop() -> TimePeriodProtocol? { let period = periods.popLast() updateExtremes() return period } internal func updateExtremes() { start = periods.first?.start end = periods.last?.end } }