Skip to content

Commit 24d285e

Browse files
committed
Add new methods to TimePeriodChain class
1 parent 57592c0 commit 24d285e

File tree

2 files changed

+268
-28
lines changed

2 files changed

+268
-28
lines changed

Sources/SwiftDate/TimePeriod/Groups/TimePeriodChain.swift

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,46 +30,69 @@ open class TimePeriodChain: TimePeriodGroup {
3030
// MARK: - Chain Existence Manipulation
3131

3232
/**
33-
* Append a TimePeriodProtocol to the periods array and update the Chain's
34-
* beginning and end.
33+
* Adds a period of equivalent length to the end of the chain, regardless of
34+
* whether the period intersects with the chain or not.
3535
*
3636
* - parameter period: TimePeriodProtocol to add to the collection
3737
*/
3838
public func append(_ period: TimePeriodProtocol) {
39-
let beginning = (periods.count > 0) ? periods.last!.end! : period.start
40-
41-
let newPeriod = TimePeriod(start: beginning!, duration: period.duration)
42-
periods.append(newPeriod)
43-
44-
//Update updateExtremes
45-
if periods.count == 1 {
46-
start = period.start
47-
end = period.end
48-
} else {
49-
end = end?.addingTimeInterval(period.duration)
39+
guard isPeriodHasExtremes(period) else {
40+
print("All TimePeriods in a TimePeriodChain must contain a defined start and end date")
41+
return;
42+
}
43+
44+
if let startDate = periods.last?.end! ?? period.start {
45+
let newPeriod = TimePeriod(start: startDate, duration: period.duration)
46+
periods.append(newPeriod)
47+
48+
updateExtremes()
5049
}
5150
}
52-
51+
5352
/**
54-
* Append a TimePeriodProtocol array to the periods array and update the Chain's
55-
* beginning and end.
53+
* Adds a periods of equivalent length of group to the end of the chain, regardless of
54+
* whether the period intersects with the chain or not.
5655
*
5756
* - parameter periodArray: TimePeriodProtocol list to add to the collection
5857
*/
5958
public func append<G: TimePeriodGroup>(contentsOf group: G) {
6059
for period in group.periods {
61-
let beginning = (periods.count > 0) ? periods.last!.end! : period.start
62-
63-
let newPeriod = TimePeriod(start: beginning!, duration: period.duration)
64-
periods.append(newPeriod)
65-
66-
//Update updateExtremes
67-
if periods.count == 1 {
68-
start = period.start
69-
end = period.end
70-
} else {
71-
end = end?.addingTimeInterval(period.duration)
72-
}
60+
append(period)
61+
}
62+
}
63+
64+
/**
65+
* Adds a period of equivalent length to the start of the chain, regardless of
66+
* whether the period intersects with the chain or not.
67+
*
68+
* - parameter period: TimePeriodProtocol to add to the collection
69+
*/
70+
public func prepend(_ period: TimePeriodProtocol) {
71+
guard isPeriodHasExtremes(period) else {
72+
print("All TimePeriods in a TimePeriodChain must contain a defined start and end date")
73+
return;
74+
}
75+
76+
if let endDate = periods.first?.start! ?? period.end {
77+
let startDate = endDate.addingTimeInterval(-period.duration)
78+
79+
let newPeriod = TimePeriod(start: startDate, duration: period.duration)
80+
periods.insert(newPeriod, at: periods.startIndex)
81+
82+
updateExtremes()
83+
}
84+
}
85+
86+
/**
87+
* Adds a periods of equivalent length of group to the start of the chain, regardless of
88+
* whether the period intersects with the chain or not.
89+
*
90+
* - parameter periodArray: TimePeriodProtocol list to add to the collection
91+
*/
92+
93+
public func prepend<G: TimePeriodGroup>(contentsOf group: G) {
94+
for period in group.periods {
95+
prepend(period)
7396
}
7497
}
7598

@@ -138,6 +161,26 @@ open class TimePeriodChain: TimePeriodGroup {
138161
start = start?.addingTimeInterval(duration)
139162
end = end?.addingTimeInterval(duration)
140163
}
164+
165+
/// Shifts chain's start date and all chain's periods to the given date
166+
///
167+
/// - Parameter date: The date to which the period's start is shifted
168+
public func shiftStart(to date: DateInRegion) {
169+
if let firstPeriodStart = periods.first?.start! {
170+
let difference = date - firstPeriodStart
171+
shift(by: difference)
172+
}
173+
}
174+
175+
/// Shifts chain's end date and all chain's periods to the given date
176+
///
177+
/// - Parameter date: The date to which the period's end is shifted
178+
public func shiftEnd(to date: DateInRegion) {
179+
if let firstPeriodEnd = periods.last?.end! {
180+
let difference = date - firstPeriodEnd
181+
shift(by: difference)
182+
}
183+
}
141184

142185
public override func map<T>(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] {
143186
return try periods.map(transform)
@@ -163,5 +206,9 @@ open class TimePeriodChain: TimePeriodGroup {
163206
start = periods.first?.start
164207
end = periods.last?.end
165208
}
209+
210+
internal func isPeriodHasExtremes (_ period: TimePeriodProtocol) -> Bool {
211+
period.start != nil && period.end != nil
212+
}
166213

167214
}

Tests/SwiftDateTests/TestTimePeriodChain.swift

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,197 @@ class TestTimePeriodChain: XCTestCase {
4343
XCTAssert(chain.periods[3].duration == originalDuration_2, "Unexpected duration of 3 period – Actual: \(chain.periods[3].duration) – Expected: \(originalDuration_2)")
4444
XCTAssert(chain.periods[4].duration == originalDuration_3, "Unexpected duration of 4 period – Actual: \(chain.periods[4].duration) – Expected: \(originalDuration_3)")
4545
}
46+
47+
func testPrependPeriod () {
48+
let period = TimePeriod(start: .init(year: 2020, month: 1, day: 1), end: .init(year: 2020, month: 1, day: 20))
49+
let chain = TimePeriodChain(periods)
50+
51+
chain.prepend(period)
52+
53+
XCTAssert(
54+
chain[0].start == DateInRegion(year: 2020, month: 1, day: 13),
55+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[0].start!) – Expected: \(DateInRegion(year: 2020, month: 1, day: 13))"
56+
)
57+
58+
XCTAssert(
59+
chain[1].start == DateInRegion(year: 2020, month: 2, day: 1),
60+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[1].start!) – Expected: \(DateInRegion(year: 2020, month: 2, day: 1))"
61+
)
62+
63+
XCTAssert(
64+
chain[2].start == DateInRegion(year: 2020, month: 3, day: 1),
65+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[2].start!) – Expected: \(DateInRegion(year: 2020, month: 3, day: 1))"
66+
)
67+
68+
XCTAssert(
69+
chain[3].start == DateInRegion(year: 2020, month: 4, day: 1),
70+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[3].start!) – Expected: \(DateInRegion(year: 2020, month: 4, day: 1))"
71+
)
72+
73+
XCTAssert(
74+
chain[4].start == DateInRegion(year: 2020, month: 5, day: 1),
75+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[4].start!) – Expected: \(DateInRegion(year: 2020, month: 5, day: 1))"
76+
)
77+
}
78+
79+
func testPrependGroup () {
80+
let testPeriods = [
81+
TimePeriod(start: .init(year: 2019, month: 10, day: 1), end: .init(year: 2019, month: 10, day: 31)),
82+
TimePeriod(start: .init(year: 2019, month: 11, day: 1), end: .init(year: 2019, month: 11, day: 30)),
83+
TimePeriod(start: .init(year: 2019, month: 12, day: 1), end: .init(year: 2019, month: 12, day: 31)),
84+
]
85+
let group = TimePeriodGroup(testPeriods)
86+
let chain = TimePeriodChain(periods)
87+
88+
chain.prepend(contentsOf: group)
89+
90+
XCTAssert(
91+
chain[0].start == DateInRegion(year: 2019, month: 11, day: 4),
92+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[0].start!) – Expected: \(DateInRegion(year: 2019, month: 11, day: 1))"
93+
)
94+
95+
XCTAssert(
96+
chain[1].start == DateInRegion(year: 2019, month: 12, day: 4),
97+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[1].start!) – Expected: \(DateInRegion(year: 2019, month: 12, day: 4))"
98+
)
99+
100+
XCTAssert(
101+
chain[2].start == DateInRegion(year: 2020, month: 1, day: 2),
102+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[2].start!) – Expected: \(DateInRegion(year: 2020, month: 1, day: 2))"
103+
)
104+
105+
XCTAssert(
106+
chain[3].start == DateInRegion(year: 2020, month: 2, day: 1),
107+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[3].start!) – Expected: \(DateInRegion(year: 2020, month: 2, day: 1))"
108+
)
109+
110+
XCTAssert(
111+
chain[4].start == DateInRegion(year: 2020, month: 3, day: 1),
112+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[4].start!) – Expected: \(DateInRegion(year: 2020, month: 3, day: 1))"
113+
)
114+
115+
XCTAssert(
116+
chain[5].start == DateInRegion(year: 2020, month: 4, day: 1),
117+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[5].start!) – Expected: \(DateInRegion(year: 2020, month: 4, day: 1))"
118+
)
119+
120+
XCTAssert(
121+
chain[6].start == DateInRegion(year: 2020, month: 5, day: 1),
122+
"Prepended TimePeriodChain contains unexpected period start date – Actual: \(chain[6].start!) – Expected: \(DateInRegion(year: 2020, month: 5, day: 1))"
123+
)
124+
}
125+
126+
func testAppendPeriod () {
127+
let period = TimePeriod(start: .init(year: 2020, month: 1, day: 1), end: .init(year: 2020, month: 1, day: 20))
128+
let chain = TimePeriodChain(periods)
129+
130+
chain.append(period)
131+
132+
XCTAssert(
133+
chain[0].end == DateInRegion(year: 2020, month: 2, day: 28),
134+
"Appended TimePeriodChain contains unexpected period end date – Actual: \(chain[0].end!) – Expected: \(DateInRegion(year: 2020, month: 2, day: 28))"
135+
)
136+
137+
XCTAssert(
138+
chain[1].end == DateInRegion(year: 2020, month: 3, day: 31),
139+
"Appended TimePeriodChain contains unexpected period end date – Actual: \(chain[1].end!) – Expected: \(DateInRegion(year: 2020, month: 3, day: 31))"
140+
)
141+
142+
XCTAssert(
143+
chain[2].end == DateInRegion(year: 2020, month: 4, day: 30),
144+
"Appended TimePeriodChain contains unexpected period end date – Actual: \(chain[2].end!) – Expected: \(DateInRegion(year: 2020, month: 4, day: 30))"
145+
)
146+
147+
XCTAssert(
148+
chain[3].end == DateInRegion(year: 2020, month: 5, day: 31),
149+
"Appended TimePeriodChain contains unexpected period end date – Actual: \(chain[3].end!) – Expected: \(DateInRegion(year: 2020, month: 5, day: 31))"
150+
)
151+
152+
XCTAssert(
153+
chain[4].end == DateInRegion(year: 2020, month: 6, day: 19),
154+
"Appended TimePeriodChain contains unexpected period end date – Actual: \(chain[4].end!) – Expected: \(DateInRegion(year: 2020, month: 6, day: 19))"
155+
)
156+
}
157+
158+
func testAppendGroup () {
159+
let testPeriods = [
160+
TimePeriod(start: .init(year: 2019, month: 10, day: 1), end: .init(year: 2019, month: 10, day: 31)),
161+
TimePeriod(start: .init(year: 2019, month: 11, day: 1), end: .init(year: 2019, month: 11, day: 30)),
162+
TimePeriod(start: .init(year: 2019, month: 12, day: 1), end: .init(year: 2019, month: 12, day: 31)),
163+
]
164+
let group = TimePeriodGroup(testPeriods)
165+
let chain = TimePeriodChain(periods)
166+
167+
chain.append(contentsOf: group)
168+
169+
XCTAssert(
170+
chain[0].end == DateInRegion(year: 2020, month: 2, day: 28),
171+
"Prepended TimePeriodChain contains unexpected period end date – Actual: \(chain[0].end!) – Expected: \(DateInRegion(year: 2020, month: 2, day: 28))"
172+
)
173+
174+
XCTAssert(
175+
chain[1].end == DateInRegion(year: 2020, month: 3, day: 31),
176+
"Prepended TimePeriodChain contains unexpected period end date – Actual: \(chain[1].end!) – Expected: \(DateInRegion(year: 2020, month: 3, day: 31))"
177+
)
178+
179+
XCTAssert(
180+
chain[2].end == DateInRegion(year: 2020, month: 4, day: 30),
181+
"Prepended TimePeriodChain contains unexpected period end date – Actual: \(chain[2].end!) – Expected: \(DateInRegion(year: 2020, month: 4, day: 30))"
182+
)
183+
184+
XCTAssert(
185+
chain[3].end == DateInRegion(year: 2020, month: 5, day: 31),
186+
"Prepended TimePeriodChain contains unexpected period end date – Actual: \(chain[3].end!) – Expected: \(DateInRegion(year: 2020, month: 5, day: 31))"
187+
)
188+
189+
XCTAssert(
190+
chain[4].end == DateInRegion(year: 2020, month: 6, day: 30),
191+
"Prepended TimePeriodChain contains unexpected period end date – Actual: \(chain[4].end!) – Expected: \(DateInRegion(year: 2020, month: 6, day: 30))"
192+
)
193+
194+
XCTAssert(
195+
chain[5].end == DateInRegion(year: 2020, month: 7, day: 29),
196+
"Prepended TimePeriodChain contains unexpected period end date – Actual: \(chain[5].end!) – Expected: \(DateInRegion(year: 2020, month: 7, day: 29))"
197+
)
198+
199+
XCTAssert(
200+
chain[6].end == DateInRegion(year: 2020, month: 8, day: 28),
201+
"Prepended TimePeriodChain contains unexpected period end date – Actual: \(chain[6].end!) – Expected: \(DateInRegion(year: 2020, month: 8, day: 28))"
202+
)
203+
}
204+
205+
func testShiftStartToDate () {
206+
let date = DateInRegion(year: 2020, month: 1, day: 1)
207+
let chain = TimePeriodChain(periods)
208+
209+
chain.shiftStart(to: date)
210+
211+
XCTAssert(
212+
chain.start == DateInRegion(year: 2020, month: 1, day: 1),
213+
"Shifted TimePeriodChain has unexpected start date – Actual: \(chain.start!) – Expected: \(DateInRegion(year: 2020, month: 1, day: 1))"
214+
)
215+
216+
XCTAssert(
217+
chain.end == DateInRegion(year: 2020, month: 4, day: 30),
218+
"Shifted TimePeriodChain has unexpected end date – Actual: \(chain.end!) – Expected: \(DateInRegion(year: 2020, month: 4, day: 30))"
219+
)
220+
}
221+
222+
func testShiftEndToDate () {
223+
let date = DateInRegion(year: 2020, month: 1, day: 1)
224+
let chain = TimePeriodChain(periods)
225+
226+
chain.shiftEnd(to: date)
227+
228+
XCTAssert(
229+
chain.start == DateInRegion(year: 2019, month: 9, day: 3),
230+
"Shifted TimePeriodChain has unexpected start date – Actual: \(chain.start!) – Expected: \(DateInRegion(year: 2019, month: 9, day: 3))"
231+
)
232+
233+
XCTAssert(
234+
chain.end == DateInRegion(year: 2020, month: 1, day: 1),
235+
"Shifted TimePeriodChain has unexpected end date – Actual: \(chain.end!) – Expected: \(DateInRegion(year: 2020, month: 1, day: 1))"
236+
)
237+
}
238+
46239
}

0 commit comments

Comments
 (0)