//
|
// 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 periods are represented by the TimePeriodProtocol protocol.
|
/// Required variables and method impleementations are bound below.
|
/// An inheritable implementation of the TimePeriodProtocol is available through the TimePeriod class.
|
open class TimePeriod: TimePeriodProtocol {
|
|
/// The start date for a TimePeriod representing the starting boundary of the time period
|
public var start: DateInRegion?
|
|
/// The end date for a TimePeriod representing the ending boundary of the time period
|
public var end: DateInRegion?
|
|
// MARK: - Initializers
|
|
public init() { }
|
|
/// Create a new time period with given date range.
|
///
|
/// - Parameters:
|
/// - start: start date
|
/// - end: end date
|
public init(start: DateInRegion?, end: DateInRegion?) {
|
self.start = start
|
self.end = end
|
}
|
|
/// Create a new time period with given start and a length specified in number of seconds.
|
///
|
/// - Parameters:
|
/// - start: start of the period
|
/// - duration: duration of the period expressed in seconds
|
public init(start: DateInRegion, duration: TimeInterval) {
|
self.start = start
|
self.end = DateInRegion(start.date.addingTimeInterval(duration), region: start.region)
|
}
|
|
/// Create a new time period which ends at given date and start date is back on time by given interval.
|
///
|
/// - Parameters:
|
/// - end: end date
|
/// - duration: duration expressed in seconds (it will be subtracted from start date)
|
public init(end: DateInRegion, duration: TimeInterval) {
|
self.end = end
|
self.start = end.addingTimeInterval(-duration)
|
}
|
|
/// Return a new instance of the TimePeriod that starts on the provided start date and is of the
|
/// size provided.
|
///
|
/// - Parameters:
|
/// - start: start of the period
|
/// - duration: length of the period (ie. `2.days` or `14.hours`...)
|
public init(start: DateInRegion, duration: DateComponents) {
|
self.start = start
|
self.end = (start + duration)
|
}
|
|
/// Return a new instance of the TimePeriod that starts at end time minus given duration.
|
///
|
/// - Parameters:
|
/// - end: end date
|
/// - duration: duration (it will be subtracted from end date in order to provide the start date)
|
public init(end: DateInRegion, duration: DateComponents) {
|
self.start = (end - duration)
|
self.end = end
|
}
|
|
/// Returns a new instance of DTTimePeriod that represents the largest time period available.
|
/// The start date is in the distant past and the end date is in the distant future.
|
///
|
/// - Returns: a new time period
|
public static func infinity() -> TimePeriod {
|
return TimePeriod(start: DateInRegion.past(), end: DateInRegion.future())
|
}
|
|
// MARK: - Shifted
|
|
/// Shift the `TimePeriod` by a `TimeInterval`
|
///
|
/// - Parameter timeInterval: The time interval to shift the period by
|
/// - Returns: The new, shifted `TimePeriod`
|
public func shifted(by timeInterval: TimeInterval) -> TimePeriod {
|
let timePeriod = TimePeriod()
|
timePeriod.start = start?.addingTimeInterval(timeInterval)
|
timePeriod.end = end?.addingTimeInterval(timeInterval)
|
return timePeriod
|
}
|
|
/// Shift the `TimePeriod` by the specified components value.
|
/// ie. `let shifted = period.shifted(by: 3.days)`
|
///
|
/// - Parameter components: components to shift
|
/// - Returns: new period
|
public func shifted(by components: DateComponents) -> TimePeriod {
|
let timePeriod = TimePeriod()
|
timePeriod.start = (hasStart ? (start! + components) : nil)
|
timePeriod.end = (hasEnd ? (end! + components) : nil)
|
return timePeriod
|
}
|
|
// MARK: - Lengthen / Shorten
|
|
/// Lengthen the `TimePeriod` by a `TimeInterval`
|
///
|
/// - Parameters:
|
/// - timeInterval: The time interval to lengthen the period by
|
/// - anchor: The anchor point from which to make the change
|
/// - Returns: The new, lengthened `TimePeriod`
|
public func lengthened(by timeInterval: TimeInterval, at anchor: TimePeriodAnchor) -> TimePeriod {
|
let timePeriod = TimePeriod()
|
switch anchor {
|
case .beginning:
|
timePeriod.start = start
|
timePeriod.end = end?.addingTimeInterval(timeInterval)
|
case .center:
|
timePeriod.start = start?.addingTimeInterval(-timeInterval)
|
timePeriod.end = end?.addingTimeInterval(timeInterval)
|
case .end:
|
timePeriod.start = start?.addingTimeInterval(-timeInterval)
|
timePeriod.end = end
|
}
|
return timePeriod
|
}
|
|
/// Shorten the `TimePeriod` by a `TimeInterval`
|
///
|
/// - Parameters:
|
/// - timeInterval: The time interval to shorten the period by
|
/// - anchor: The anchor point from which to make the change
|
/// - Returns: The new, shortened `TimePeriod`
|
public func shortened(by timeInterval: TimeInterval, at anchor: TimePeriodAnchor) -> TimePeriod {
|
let timePeriod = TimePeriod()
|
switch anchor {
|
case .beginning:
|
timePeriod.start = start
|
timePeriod.end = end?.addingTimeInterval(-timeInterval)
|
case .center:
|
timePeriod.start = start?.addingTimeInterval(-timeInterval / 2)
|
timePeriod.end = end?.addingTimeInterval(timeInterval / 2)
|
case .end:
|
timePeriod.start = start?.addingTimeInterval(timeInterval)
|
timePeriod.end = end
|
}
|
return timePeriod
|
}
|
|
// MARK: - Operator Overloads
|
|
/// Default anchor = beginning
|
/// Operator overload for lengthening a `TimePeriod` by a `TimeInterval`
|
public static func + (leftAddend: TimePeriod, rightAddend: TimeInterval) -> TimePeriod {
|
return leftAddend.lengthened(by: rightAddend, at: .beginning)
|
}
|
|
/// Default anchor = beginning
|
/// Operator overload for shortening a `TimePeriod` by a `TimeInterval`
|
public static func - (minuend: TimePeriod, subtrahend: TimeInterval) -> TimePeriod {
|
return minuend.shortened(by: subtrahend, at: .beginning)
|
}
|
|
/// Operator overload for checking if a `TimePeriod` is equal to a `TimePeriodProtocol`
|
public static func == (left: TimePeriod, right: TimePeriodProtocol) -> Bool {
|
return left.equals(right)
|
}
|
|
}
|
|
public extension TimePeriod {
|
|
/// The start date of the time period
|
var startDate: Date? {
|
return start?.date
|
}
|
|
/// The end date of the time period
|
var endDate: Date? {
|
return end?.date
|
}
|
|
/// Create a new time period with the given start date, end date and region (default is UTC)
|
convenience init(startDate: Date, endDate: Date, region: Region = Region.UTC) {
|
let start = DateInRegion(startDate, region: region)
|
let end = DateInRegion(endDate, region: region)
|
self.init(start: start, end: end)
|
}
|
}
|