Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix TimePeriodChain extremes after initialization #773

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 86 additions & 31 deletions Sources/SwiftDate/TimePeriod/Groups/TimePeriodChain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,50 +18,81 @@ import Foundation
/// 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: - Initializers

public override init(_ periods: [TimePeriodProtocol]? = nil) {
super.init(periods)

updateExtremes()
}

// MARK: - Chain Existence Manipulation

/**
* Append a TimePeriodProtocol to the periods array and update the Chain's
* beginning and end.
* Adds a period of equivalent length to the end of the chain, regardless of
* whether the period intersects with the chain or not.
*
* - 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)
guard isPeriodHasExtremes(period) else {
print("All TimePeriods in a TimePeriodChain must contain a defined start and end date")
return;
}

if let startDate = periods.last?.end! ?? period.start {
let newPeriod = TimePeriod(start: startDate, duration: period.duration)
periods.append(newPeriod)

updateExtremes()
}
}

/**
* Append a TimePeriodProtocol array to the periods array and update the Chain's
* beginning and end.
* Adds a periods of equivalent length of group to the end of the chain, regardless of
* whether the period intersects with the chain or not.
*
* - parameter periodArray: TimePeriodProtocol list to add to the collection
*/
public func append<G: TimePeriodGroup>(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)
}
append(period)
}
}

/**
* Adds a period of equivalent length to the start of the chain, regardless of
* whether the period intersects with the chain or not.
*
* - parameter period: TimePeriodProtocol to add to the collection
*/
public func prepend(_ period: TimePeriodProtocol) {
guard isPeriodHasExtremes(period) else {
print("All TimePeriods in a TimePeriodChain must contain a defined start and end date")
return;
}

if let endDate = periods.first?.start! ?? period.end {
let startDate = endDate.addingTimeInterval(-period.duration)

let newPeriod = TimePeriod(start: startDate, duration: period.duration)
periods.insert(newPeriod, at: periods.startIndex)

updateExtremes()
}
}

/**
* Adds a periods of equivalent length of group to the start of the chain, regardless of
* whether the period intersects with the chain or not.
*
* - parameter periodArray: TimePeriodProtocol list to add to the collection
*/

public func prepend<G: TimePeriodGroup>(contentsOf group: G) {
for period in group.periods {
prepend(period)
}
}

Expand All @@ -86,9 +117,9 @@ open class TimePeriodChain: TimePeriodGroup {
//Shift all periods after inserted period
for i in 0..<periods.count {
if i > index && i > 0 {
let currentPeriod = TimePeriod(start: period.start, end: period.end)
let duration = periods[i].duration
periods[i].start = periods[i - 1].end
periods[i].end = periods[i].start!.addingTimeInterval(currentPeriod.duration)
periods[i].end = periods[i].start!.addingTimeInterval(duration)
}
}

Expand Down Expand Up @@ -130,6 +161,26 @@ open class TimePeriodChain: TimePeriodGroup {
start = start?.addingTimeInterval(duration)
end = end?.addingTimeInterval(duration)
}

/// Shifts chain's start date and all chain's periods to the given date
///
/// - Parameter date: The date to which the period's start is shifted
public func shiftStart(to date: DateInRegion) {
if let firstPeriodStart = periods.first?.start! {
let difference = date - firstPeriodStart
shift(by: difference)
}
}

/// Shifts chain's end date and all chain's periods to the given date
///
/// - Parameter date: The date to which the period's end is shifted
public func shiftEnd(to date: DateInRegion) {
if let firstPeriodEnd = periods.last?.end! {
let difference = date - firstPeriodEnd
shift(by: difference)
}
}

public override func map<T>(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] {
return try periods.map(transform)
Expand All @@ -155,5 +206,9 @@ open class TimePeriodChain: TimePeriodGroup {
start = periods.first?.start
end = periods.last?.end
}

internal func isPeriodHasExtremes (_ period: TimePeriodProtocol) -> Bool {
period.start != nil && period.end != nil
}

}
8 changes: 8 additions & 0 deletions SwiftDate.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
3A825B6325FEC54700F21DB2 /* TestTimePeriodChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A825B4925FEC54100F21DB2 /* TestTimePeriodChain.swift */; };
3A825B6425FEC54800F21DB2 /* TestTimePeriodChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A825B4925FEC54100F21DB2 /* TestTimePeriodChain.swift */; };
3A825B6525FEC54800F21DB2 /* TestTimePeriodChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A825B4925FEC54100F21DB2 /* TestTimePeriodChain.swift */; };
52D6D9871BEFF229002C0205 /* SwiftDate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SwiftDate.framework */; };
6434DD6120C7FAF6007626EF /* DateInRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6434DD6020C7FAF6007626EF /* DateInRegion.swift */; };
6434DD6220C7FAF6007626EF /* DateInRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6434DD6020C7FAF6007626EF /* DateInRegion.swift */; };
Expand Down Expand Up @@ -258,6 +261,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
3A825B4925FEC54100F21DB2 /* TestTimePeriodChain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTimePeriodChain.swift; sourceTree = "<group>"; };
52D6D97C1BEFF229002C0205 /* SwiftDate.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftDate.framework; sourceTree = BUILT_PRODUCTS_DIR; };
52D6D9861BEFF229002C0205 /* SwiftDate-iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftDate-iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
52D6D9E21BEFFF6E002C0205 /* SwiftDate.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftDate.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -569,6 +573,7 @@
64BAB12720E6411100FEED79 /* TestSwiftDate.swift */,
647AD65B21F4851F00CF787E /* TestDataStructures.swift */,
A89F3FAE22A00019002D1BD0 /* TestDate.swift */,
3A825B4925FEC54100F21DB2 /* TestTimePeriodChain.swift */,
);
name = Tests;
path = Tests/SwiftDateTests;
Expand Down Expand Up @@ -974,6 +979,7 @@
64BAB12420E63A3A00FEED79 /* TestDateInRegion+Langs.swift in Sources */,
647AD65C21F4851F00CF787E /* TestDataStructures.swift in Sources */,
6439232220D912670098EC03 /* TestDateInRegion+Math.swift in Sources */,
3A825B6525FEC54800F21DB2 /* TestTimePeriodChain.swift in Sources */,
64EF3E0F20D65478002793C6 /* TestDateInRegion+Compare.swift in Sources */,
6439232620D91D170098EC03 /* TestFormatters.swift in Sources */,
64EF3E0B20D65329002793C6 /* TestDateInRegion+Create.swift in Sources */,
Expand Down Expand Up @@ -1163,6 +1169,7 @@
buildActionMask = 2147483647;
files = (
64BAB12520E63A3A00FEED79 /* TestDateInRegion+Langs.swift in Sources */,
3A825B6425FEC54800F21DB2 /* TestTimePeriodChain.swift in Sources */,
647AD65D21F4851F00CF787E /* TestDataStructures.swift in Sources */,
A89F3FAF22A00019002D1BD0 /* TestDate.swift in Sources */,
6439232320D912670098EC03 /* TestDateInRegion+Math.swift in Sources */,
Expand All @@ -1183,6 +1190,7 @@
64BAB12620E63A3A00FEED79 /* TestDateInRegion+Langs.swift in Sources */,
647AD65E21F4851F00CF787E /* TestDataStructures.swift in Sources */,
6439232420D912670098EC03 /* TestDateInRegion+Math.swift in Sources */,
3A825B6325FEC54700F21DB2 /* TestTimePeriodChain.swift in Sources */,
64EF3E1120D65478002793C6 /* TestDateInRegion+Compare.swift in Sources */,
6439232820D91D170098EC03 /* TestFormatters.swift in Sources */,
64EF3E0D20D65329002793C6 /* TestDateInRegion+Create.swift in Sources */,
Expand Down
Loading