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

DateInterval time and date interval difference APIs #330

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
133 changes: 133 additions & 0 deletions Sources/FoundationEssentials/DateInterval.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,139 @@ public struct DateInterval : Comparable, Hashable, Codable, Sendable {
}
return false
}

/**
Returns the seconds between `self` and `date` or `nil` if there is no difference in time between them.

For example, given this interval and this date on a timeline:
```
|-----| <-- time interval --> *
```
Returns a negative time interval when `date` is a moment greater than or equal to the end of `self` because the receiver specifies a range of times earlier than `date`.

```
* <-- time interval --> |-----|
```
Returns a positive time interval when `date` is a moment less than or equal to (before) the start of `self` because the receiver specifies a range of times later than `date`.

A return value of `0` indicates `date` is equal to either the start or end moments of `self`.

A return value of `nil` indicates the `date` is between the start and end dates (`date` is both greater than the start and less than the end moments of `self`):
```
|--*--|
```
*/
public func timeIntervalSince(_ date: Date) -> TimeInterval? {
if end <= date {
return end.timeIntervalSince(date)
} else if date <= start {
return start.timeIntervalSince(date)
} else {
return nil
}
}

/**
Returns the date interval between `self` and `date` or `nil` if there is no difference in time between them.

For example, given this interval and this date on a timeline:
```
* <-- duration --> |-----|
```
Returns a value whose start is `date` and whose `duration` is the time between the `date` and the end of `self`.

```
|-----| <-- duration --> *
```
Returns a value whose start is the end of `self` and whose `duration` is the time between the `date` and the the end of `self`.

A return value with a duration of `0` indicates `date` is equal to the start or end of `self`.

A return value of `nil` indicates there are no moments between `date` and `self` (`date` is both greater than the start and less than the end moments of `self`):
```
|--*--|
```
*/
public func dateIntervalSince(_ date: Date) -> DateInterval? {
if date <= start {
return DateInterval(start: date, end: start)

} else if end <= date {
return DateInterval(start: end, end: date)

} else {
return nil
}
}

/**
Returns the seconds between `self` and `dateInterval` or `nil` if there is no difference in time between them.

For example, given these two intervals on a timeline:
```
|-----| <-- time interval --> |-----|
```
Returns a negative time interval when `self` ends before `dateInterval` starts. A postive time interval indicates `self` starts after `dateInterval` ends.

A return value of `0` indicates `self` starts or ends where `dateInterval` ends or starts (in other words, they intersect at their opposing start/end moments):
```
|-----|-----|
```

A return value of `nil` indicates `self` and `dateInterval` do not have any time between them:
```
|--|-----|--|
```
*/
public func timeIntervalSince(_ dateInterval: DateInterval) -> TimeInterval? {
if end <= dateInterval.start {
return end.timeIntervalSince(dateInterval.start)

} else if dateInterval.end <= start {
return start.timeIntervalSince(dateInterval.end)

} else {
return nil
}
}

/**
Returns the date interval between `self` and `dateInterval` or `nil` if there is no difference in time between them.

For example, given these two intervals on a timeline:
```
|-----| <-- duration --> |-----|
```
The latest start date and the earliest end date between `self` and `dateInterval` is determined. Returns a date interval whose start is the earliest end date and whose duration is the difference in time between the latest start and earliest end.

A return value with a duration of `0` indicates `self` and `dateInterval` form an unbroken, continous interval (in other words, they intersect at opposing starts/ends):
```
|-----|-----|
```

A return value of `nil` indicates that no interval exists between `self` and `dateInterval`:
```
|--|-----|--|
```
*/
public func dateIntervalSince(_ dateInterval: DateInterval) -> DateInterval? {
let earliestEnd: Date
let duration: TimeInterval

if end <= dateInterval.start {
earliestEnd = end
duration = dateInterval.start.timeIntervalSince(end)

} else if dateInterval.end <= start {
earliestEnd = dateInterval.end
duration = start.timeIntervalSince(dateInterval.end)

} else {
return nil
}

return DateInterval(start: earliestEnd, duration: duration)
}

public func hash(into hasher: inout Hasher) {
hasher.combine(start)
Expand Down
134 changes: 134 additions & 0 deletions Tests/FoundationEssentialsTests/DateIntervalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,140 @@ final class DateIntervalTests : XCTestCase {
let testInterval3 = DateInterval(start: start, duration: 100.0)
XCTAssertNotEqual(testInterval1, testInterval3)
}

func test_intervalsBetweenDateIntervalAndDate() {
let earlier = Date(timeIntervalSince1970: 0)
let middle = Date(timeIntervalSince1970: 5)
let later = Date(timeIntervalSince1970: 10)

let start = Date(timeIntervalSince1970: 1)
let duration: TimeInterval = 8
let end = start.addingTimeInterval(duration) // 9
let testInterval1 = DateInterval(start: start, duration: duration)

// * --- |testInterval1|
let t1 = testInterval1.timeIntervalSince(earlier)
let d1 = testInterval1.dateIntervalSince(earlier)
XCTAssertEqual(t1, 1)
XCTAssertEqual(d1, DateInterval(start: earlier, end: start))

// |testInterval1| --- *
let t2 = testInterval1.timeIntervalSince(later)
let d2 = testInterval1.dateIntervalSince(later)
XCTAssertEqual(t2, -1)
XCTAssertEqual(d2, DateInterval(start: end, end: later))

// | testInterval1 * |
let t3 = testInterval1.timeIntervalSince(middle)
let d3 = testInterval1.dateIntervalSince(middle)
XCTAssertEqual(t3, nil)
XCTAssertEqual(d3, nil)

// equal to start/end
XCTAssertEqual(testInterval1.timeIntervalSince(start), 0)
XCTAssertEqual(testInterval1.dateIntervalSince(start), DateInterval(start: start, duration: 0))
XCTAssertEqual(testInterval1.timeIntervalSince(end), 0)
XCTAssertEqual(testInterval1.dateIntervalSince(end), DateInterval(start: end, duration: 0))
}

func test_intervalsBetweenDateIntervals() {
// Tests for intervals of zero or more duration between subjects.
// |testInterval1|testInterval2|
let testInterval1 = DateInterval(start: Date(timeIntervalSince1970: 0), end: Date(timeIntervalSinceReferenceDate: 0))
let testInterval2 = DateInterval(start: Date(timeIntervalSinceReferenceDate: 0), end: Date(timeIntervalSinceReferenceDate: 100))
let t1 = testInterval1.timeIntervalSince(testInterval2)
XCTAssertEqual(t1, 0)

let t2 = testInterval2.timeIntervalSince(testInterval1)
XCTAssertEqual(t2, 0)

let d1 = testInterval1.dateIntervalSince(testInterval2)
XCTAssertEqual(d1?.start, testInterval1.end)
XCTAssertEqual(d1?.duration, 0)

let d2 = testInterval2.dateIntervalSince(testInterval1)
XCTAssertEqual(d2?.start, testInterval1.end)
XCTAssertEqual(d2?.duration, 0)

XCTAssertEqual(d1, d2)

// |testInterval3|-----|testInterval4|
let testInterval3 = DateInterval(start: Date(timeIntervalSince1970: 0), end: Date(timeIntervalSinceReferenceDate: 0))
let testInterval4 = DateInterval(start: Date(timeIntervalSinceReferenceDate: 1), end: Date(timeIntervalSinceReferenceDate: 100))
let t3 = testInterval3.timeIntervalSince(testInterval4)
XCTAssertEqual(t3, -1)

let t4 = testInterval4.timeIntervalSince(testInterval3)
XCTAssertEqual(t4, 1)

let d3 = testInterval3.dateIntervalSince(testInterval4)
let d4 = testInterval4.dateIntervalSince(testInterval3)
XCTAssertEqual(d3?.duration, 1)
XCTAssertEqual(d3?.start, testInterval3.end)
XCTAssertEqual(d4?.duration, 1)
XCTAssertEqual(d4?.start, testInterval3.end)

// Tests for non-existing intervals between subjects.
// |testInterval5|
// |testInterval6|
//
// As a single timeline: |555|565656|666|
let testInterval5 = DateInterval(start: Date(timeIntervalSince1970: 0), end: Date(timeIntervalSinceReferenceDate: 0))
let testInterval6 = DateInterval(start: Date(timeIntervalSinceReferenceDate: -1), end: Date(timeIntervalSinceReferenceDate: 100))
let t5 = testInterval5.timeIntervalSince(testInterval6)
XCTAssertEqual(t5, nil)

let t6 = testInterval6.timeIntervalSince(testInterval5)
XCTAssertEqual(t6, nil)

let d5 = testInterval5.dateIntervalSince(testInterval6)
XCTAssertEqual(d5, nil)

let d6 = testInterval6.dateIntervalSince(testInterval5)
XCTAssertEqual(d6, nil)

// |---testInterval7---|
// |testInterval8|
//
// As a single timeline: |777|787878|777|
let testInterval7 = DateInterval(start: Date(timeIntervalSince1970: 0), end: Date(timeIntervalSinceReferenceDate: 0))
let testInterval8 = DateInterval(start: Date(timeIntervalSince1970: 10), end: Date(timeIntervalSince1970: 20))
let t7 = testInterval7.timeIntervalSince(testInterval8)
XCTAssertEqual(t7, nil)

let t8 = testInterval8.timeIntervalSince(testInterval7)
XCTAssertEqual(t8, nil)

let d7 = testInterval7.dateIntervalSince(testInterval8)
XCTAssertEqual(d7, nil)

let d8 = testInterval8.dateIntervalSince(testInterval7)
XCTAssertEqual(d8, nil)

// |testInterval9|
// |testInterval10---|
let testInterval9 = DateInterval(start: Date(timeIntervalSince1970: 0), end: Date(timeIntervalSinceReferenceDate: 0))
let testInterval10 = DateInterval(start: Date(timeIntervalSince1970: 0), end: Date(timeIntervalSinceReferenceDate: 100))
let t9 = testInterval9.timeIntervalSince(testInterval10)
XCTAssertEqual(t9, nil)

let t10 = testInterval10.timeIntervalSince(testInterval9)
XCTAssertEqual(t10, nil)

let d9 = testInterval9.dateIntervalSince(testInterval10)
XCTAssertEqual(d9, nil)

let d10 = testInterval10.dateIntervalSince(testInterval9)
XCTAssertEqual(d10, nil)

// |testInterval11| on itself
let testInterval11 = DateInterval(start: Date(timeIntervalSince1970: 0), end: Date(timeIntervalSinceReferenceDate: 0))
let t11 = testInterval11.timeIntervalSince(testInterval11)
XCTAssertEqual(t11, nil)

let d11 = testInterval11.dateIntervalSince(testInterval11)
XCTAssertEqual(d11, nil)
}

func test_hashing() {
// dateWithString("2019-04-04 17:09:23 -0700")
Expand Down