宽窄优行-由【嘉易行】项目成品而来
younger_times
2023-04-06 a1ae6802080a22e6e6ce6d0935e95facb1daca5c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//
//  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)
    }
}