Skip to content

Commit d74cd6b

Browse files
committed
TimePeriodChain – update append method implementation, add prepend method, add shift to method
1 parent 8cfed3b commit d74cd6b

File tree

2 files changed

+267
-29
lines changed

2 files changed

+267
-29
lines changed

Sources/SwiftDate/TimePeriod/Groups/TimePeriodChain.swift

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,48 +28,71 @@ open class TimePeriodChain: TimePeriodGroup {
2828
}
2929

3030
// MARK: - Chain Existence Manipulation
31-
31+
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: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,195 @@ class TestTimePeriodChain: XCTestCase {
2626
XCTAssert(chain.end == periods.last!.end, "TimePeriodChain initial end not equals to source period end")
2727
}
2828

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

0 commit comments

Comments
 (0)