From 05617caee6e757014a49af7753776499b9a75eda Mon Sep 17 00:00:00 2001 From: QuentinJin Date: Sun, 26 Aug 2018 17:56:07 +0800 Subject: [PATCH] Add "compare" method for Interval, and more tests --- Schedule.xcodeproj/project.pbxproj | 22 ++- Sources/Schedule/Interval.swift | 199 +++++++++++++----------- Sources/Schedule/Time.swift | 2 +- Tests/ScheduleTests/DateTimeTests.swift | 117 +++++++++----- 4 files changed, 206 insertions(+), 134 deletions(-) diff --git a/Schedule.xcodeproj/project.pbxproj b/Schedule.xcodeproj/project.pbxproj index 0855236..1d40694 100644 --- a/Schedule.xcodeproj/project.pbxproj +++ b/Schedule.xcodeproj/project.pbxproj @@ -151,7 +151,7 @@ name = Utils; sourceTree = ""; }; - 668685F5210DD226009305C3 /* Datetime */ = { + 668685F5210DD226009305C3 /* DateTime */ = { isa = PBXGroup; children = ( OBJ_12 /* Interval.swift */, @@ -160,7 +160,18 @@ OBJ_20 /* Time.swift */, OBJ_23 /* Weekday.swift */, ); - name = Datetime; + name = DateTime; + sourceTree = ""; + }; + 6695C99A21329DDB00934290 /* UtilsTests */ = { + isa = PBXGroup; + children = ( + OBJ_34 /* WeakSetTests.swift */, + OBJ_26 /* AtomicTests.swift */, + OBJ_27 /* BucketTests.swift */, + OBJ_29 /* ExtensionsTests.swift */, + ); + name = UtilsTests; sourceTree = ""; }; OBJ_24 /* Tests */ = { @@ -174,16 +185,13 @@ OBJ_25 /* ScheduleTests */ = { isa = PBXGroup; children = ( - OBJ_26 /* AtomicTests.swift */, - OBJ_27 /* BucketTests.swift */, OBJ_28 /* DateTimeTests.swift */, - OBJ_29 /* ExtensionsTests.swift */, OBJ_30 /* Misc.swift */, OBJ_31 /* SchedulesTests.swift */, OBJ_32 /* TaskCenterTests.swift */, OBJ_33 /* TaskTests.swift */, - OBJ_34 /* WeakSetTests.swift */, OBJ_35 /* XCTestManifests.swift */, + 6695C99A21329DDB00934290 /* UtilsTests */, ); name = ScheduleTests; path = Tests/ScheduleTests; @@ -226,7 +234,7 @@ OBJ_18 /* Task.swift */, OBJ_19 /* TaskCenter.swift */, OBJ_21 /* Timeline.swift */, - 668685F5210DD226009305C3 /* Datetime */, + 668685F5210DD226009305C3 /* DateTime */, 668685F4210DD21A009305C3 /* Utils */, ); name = Schedule; diff --git a/Sources/Schedule/Interval.swift b/Sources/Schedule/Interval.swift index 67339e3..d23c81c 100644 --- a/Sources/Schedule/Interval.swift +++ b/Sources/Schedule/Interval.swift @@ -17,6 +17,10 @@ public struct Interval { public init(nanoseconds: Double) { self.nanoseconds = nanoseconds } +} + +// MARK: - Describing +extension Interval { /// A boolean value indicating whether this interval is less than zero. /// @@ -49,8 +53,46 @@ public struct Interval { } } +extension Interval: Hashable { + + /// The hashValue of this interval. + public var hashValue: Int { + return nanoseconds.hashValue + } + + /// Returns a boolean value indicating whether two intervals are equal. + public static func == (lhs: Interval, rhs: Interval) -> Bool { + return lhs.nanoseconds == rhs.nanoseconds + } +} + +extension Interval: CustomStringConvertible { + + /// A textual representation of this interval. + public var description: String { + return "Interval: \(nanoseconds.clampedToInt()) nanoseconds" + } +} + +extension Interval: CustomDebugStringConvertible { + + /// A textual representation of this interval for debugging. + public var debugDescription: String { + return description + } +} + +// MARK: - Comparing extension Interval { + /// Compares two intervals. + /// + /// A positive interval is always ordered ascending to a negative interval. + public func compare(_ other: Interval) -> ComparisonResult { + let now = Date() + return now.adding(self).compare(now.adding(other)) + } + /// Returns a boolean value indicating whether this interval is longer /// than the given value. public func isLonger(than other: Interval) -> Bool { @@ -72,7 +114,10 @@ extension Interval { public static func shortest(_ intervals: Interval...) -> Interval { return intervals.sorted(by: { $0.magnitude < $1.magnitude })[0] } +} +// MARK: - Adding & Subtracting +extension Interval { /// Returns a new interval by multipling this interval by the given number. /// /// 1.hour * 2 == 2.hours @@ -95,6 +140,42 @@ extension Interval { } } +// MARK: - Operators +extension Interval { + + /// Returns a new interval by multipling the left interval by the right number. + /// + /// 1.hour * 2 == 2.hours + public static func * (lhs: Interval, rhs: Double) -> Interval { + return lhs.multiplying(by: rhs) + } + + /// Returns a new interval by adding the right interval to the left interval. + /// + /// 1.hour + 1.hour == 2.hours + public static func + (lhs: Interval, rhs: Interval) -> Interval { + return lhs.adding(rhs) + } + + /// Returns a new interval by subtracting the right interval from the left interval. + /// + /// 2.hours - 1.hour == 1.hour + public static func - (lhs: Interval, rhs: Interval) -> Interval { + return lhs.subtracting(rhs) + } + + /// Adds two intervals and stores the result in the first interval. + public static func += (lhs: inout Interval, rhs: Interval) { + lhs = lhs.adding(rhs) + } + + /// Returns the additive inverse of the specified interval. + public prefix static func - (interval: Interval) -> Interval { + return interval.opposite + } +} + +// MARK: - Sugars extension Interval { /// Creates an interval from the given number of seconds. @@ -138,96 +219,6 @@ extension Interval { } } -extension Interval { - - /// Returns a new interval by multipling the left interval by the right number. - /// - /// 1.hour * 2 == 2.hours - public static func * (lhs: Interval, rhs: Double) -> Interval { - return lhs.multiplying(by: rhs) - } - - /// Returns a new interval by adding the right interval to the left interval. - /// - /// 1.hour + 1.hour == 2.hours - public static func + (lhs: Interval, rhs: Interval) -> Interval { - return lhs.adding(rhs) - } - - /// Returns a new interval by subtracting the right interval from the left interval. - /// - /// 2.hours - 1.hour == 1.hour - public static func - (lhs: Interval, rhs: Interval) -> Interval { - return lhs.subtracting(rhs) - } - - /// Adds two intervals and stores the result in the first interval. - public static func += (lhs: inout Interval, rhs: Interval) { - lhs = lhs.adding(rhs) - } - - /// Returns the additive inverse of the specified interval. - public prefix static func - (interval: Interval) -> Interval { - return interval.opposite - } -} - -extension Interval: Hashable { - - /// The hashValue of this interval. - public var hashValue: Int { - return nanoseconds.hashValue - } - - /// Returns a boolean value indicating whether two intervals are equal. - public static func == (lhs: Interval, rhs: Interval) -> Bool { - return lhs.nanoseconds == rhs.nanoseconds - } -} - -extension Interval: CustomStringConvertible { - - /// A textual representation of this interval. - public var description: String { - return "Interval: \(nanoseconds.clampedToInt()) nanoseconds" - } -} - -extension Interval: CustomDebugStringConvertible { - - /// A textual representation of this interval for debugging. - public var debugDescription: String { - return description - } -} - -extension Date { - - /// The interval between this date and the current date and time. - /// - /// If this date is earlier than now, this interval will be negative. - public var intervalSinceNow: Interval { - return timeIntervalSinceNow.seconds - } - - /// Returns the interval between this date and the given date. - /// - /// If this date is earlier than the given date, this interval will be negative. - public func interval(since date: Date) -> Interval { - return timeIntervalSince(date).seconds - } - - /// Returns a new date by adding an interval to this date. - public func adding(_ interval: Interval) -> Date { - return addingTimeInterval(interval.seconds) - } - - /// Returns a date with an interval added to it. - public static func + (lhs: Date, rhs: Interval) -> Date { - return lhs.adding(rhs) - } -} - /// `IntervalConvertible` provides a set of intuitive apis for creating interval. public protocol IntervalConvertible { @@ -311,6 +302,33 @@ extension IntervalConvertible { } } +extension Date { + + /// The interval between this date and the current date and time. + /// + /// If this date is earlier than now, this interval will be negative. + public var intervalSinceNow: Interval { + return timeIntervalSinceNow.seconds + } + + /// Returns the interval between this date and the given date. + /// + /// If this date is earlier than the given date, this interval will be negative. + public func interval(since date: Date) -> Interval { + return timeIntervalSince(date).seconds + } + + /// Returns a new date by adding an interval to this date. + public func adding(_ interval: Interval) -> Date { + return addingTimeInterval(interval.seconds) + } + + /// Returns a date with an interval added to it. + public static func + (lhs: Date, rhs: Interval) -> Date { + return lhs.adding(rhs) + } +} + extension DispatchSourceTimer { func schedule(after delay: Interval) { @@ -318,7 +336,6 @@ extension DispatchSourceTimer { schedule(wallDeadline: .distantFuture) return } - let ns = delay.nanoseconds.clampedToInt() schedule(wallDeadline: .now() + DispatchTimeInterval.nanoseconds(ns)) } diff --git a/Sources/Schedule/Time.swift b/Sources/Schedule/Time.swift index c8d8069..48b2548 100644 --- a/Sources/Schedule/Time.swift +++ b/Sources/Schedule/Time.swift @@ -114,7 +114,7 @@ public struct Time { /// The interval between this time and zero o'clock. public var intervalSinceZeroClock: Interval { - return Int(hour).hours + Int(minute).minutes + Int(second).seconds + Int(nanosecond).nanoseconds + return hour.hours + minute.minutes + second.seconds + nanosecond.nanoseconds } func toDateComponents() -> DateComponents { diff --git a/Tests/ScheduleTests/DateTimeTests.swift b/Tests/ScheduleTests/DateTimeTests.swift index f64138e..7e0864d 100644 --- a/Tests/ScheduleTests/DateTimeTests.swift +++ b/Tests/ScheduleTests/DateTimeTests.swift @@ -13,26 +13,89 @@ final class DateTimeTests: XCTestCase { func testInterval() { XCTAssertTrue((-1).second.isNegative) + XCTAssertTrue(1.second.isPositive) XCTAssertEqual(1.1.second.magnitude, 1.1.second.nanoseconds) + XCTAssertEqual(1.second.opposite, (-1).second) + + XCTAssertEqual(7.day.hashValue, 1.week.hashValue) + XCTAssertEqual(7.day, 1.week) + + XCTAssertEqual((-2).seconds.compare(1.second), .orderedAscending) XCTAssertTrue(1.1.second.isLonger(than: 1.0.second)) XCTAssertTrue(3.days.isShorter(than: 1.week)) XCTAssertEqual(Interval.longest(1.hour, 1.day, 1.week), 1.week) - XCTAssertEqual(Interval.shortest(1.hour, 59.minutes, 3000.seconds), 3000.seconds) + XCTAssertEqual(Interval.shortest(1.hour, 59.minutes, 2999.seconds), 2999.seconds) XCTAssertEqual(1.second * 60, 1.minute) XCTAssertEqual(59.minutes + 60.seconds, 1.hour) XCTAssertEqual(1.week - 24.hours, 6.days) + var i0 = 1.day + i0 += 1.day + XCTAssertEqual(i0, 2.days) XCTAssertEqual(-(1.second), (-1).second) - XCTAssertEqual(1.nanoseconds, Interval(nanoseconds: 1)) - XCTAssertEqual(2.microseconds, Interval(nanoseconds: 2.microseconds.nanoseconds)) - XCTAssertEqual(3.milliseconds, Interval(nanoseconds: 3.milliseconds.nanoseconds)) - XCTAssertEqual(4.seconds, Interval(nanoseconds: 4.seconds.nanoseconds)) - XCTAssertEqual(5.1.minutes, Interval(nanoseconds: 5.1.minutes.nanoseconds)) - XCTAssertEqual(6.2.hours, Interval(nanoseconds: 6.2.hours.nanoseconds)) - XCTAssertEqual(7.3.days, Interval(nanoseconds: 7.3.days.nanoseconds)) - XCTAssertEqual(8.4.weeks, Interval(nanoseconds: 8.4.weeks.nanoseconds)) + let i1 = Interval(seconds: 24 * 60 * 60) + XCTAssertEqual(1.nanosecond * i1.nanoseconds, 1.day) + XCTAssertEqual(2.microsecond * i1.microseconds, 2.days) + XCTAssertEqual(3.millisecond * i1.milliseconds, 3.days) + XCTAssertEqual(4.second * i1.seconds, 4.days) + XCTAssertEqual(5.1.minute * i1.minutes, 5.1.days) + XCTAssertEqual(6.2.hour * i1.hours, 6.2.days) + XCTAssertEqual(7.3.day * i1.days, 7.3.days) + XCTAssertEqual(1.week * i1.weeks, 1.days) + + let date0 = Date() + let date1 = date0.addingTimeInterval(100) + XCTAssertEqual(date0.interval(since: date1), date0.timeIntervalSince(date1).seconds) + XCTAssertEqual(date0.adding(1.seconds), date0.addingTimeInterval(1)) + XCTAssertEqual(date0 + 1.seconds, date0.addingTimeInterval(1)) + } + + func testMonthday() { + XCTAssertEqual(Monthday.january(1).toDateComponents().month, 1) + XCTAssertEqual(Monthday.february(1).toDateComponents().month, 2) + XCTAssertEqual(Monthday.march(1).toDateComponents().month, 3) + XCTAssertEqual(Monthday.april(1).toDateComponents().month, 4) + XCTAssertEqual(Monthday.may(1).toDateComponents().month, 5) + XCTAssertEqual(Monthday.june(1).toDateComponents().month, 6) + XCTAssertEqual(Monthday.july(1).toDateComponents().month, 7) + XCTAssertEqual(Monthday.august(1).toDateComponents().month, 8) + XCTAssertEqual(Monthday.september(1).toDateComponents().month, 9) + XCTAssertEqual(Monthday.october(1).toDateComponents().month, 10) + XCTAssertEqual(Monthday.november(1).toDateComponents().month, 11) + XCTAssertEqual(Monthday.december(1).toDateComponents().month, 12) + } + + func testPeriod() { + let p0 = (1.year + 2.years + 1.month + 2.months + 3.days).tidied(to: .day) + XCTAssertEqual(p0.years, 3) + XCTAssertEqual(p0.months, 3) + XCTAssertEqual(p0.days, 3) + + let p1 = Period("one second")?.tidied(to: .second) + XCTAssertNotNil(p1) + XCTAssertEqual(p1!.seconds, 1) + let p2 = Period("two hours and ten minutes")?.tidied(to: .day) + XCTAssertNotNil(p2) + XCTAssertEqual(p2!.hours, 2) + XCTAssertEqual(p2!.minutes, 10) + let p3 = Period("1 year, 2 months and 3 days")?.tidied(to: .day) + XCTAssertNotNil(p3) + XCTAssertEqual(p3!.years, 1) + XCTAssertEqual(p3!.months, 2) + XCTAssertEqual(p3!.days, 3) + + Period.registerQuantifier("many", for: 100 * 1000) + let p4 = Period("many days") + XCTAssertEqual(p4!.days, 100 * 1000) + + let date = Date(year: 1989, month: 6, day: 4) + 1.year + let year = date.dateComponents.year + XCTAssertEqual(year, 1990) + + let p5 = Period(hours: 25).tidied(to: .day) + XCTAssertEqual(p5.days, 1) } func testTime() { @@ -55,38 +118,22 @@ final class DateTimeTests: XCTestCase { let t3 = Time("12 am") XCTAssertNotNil(t3) XCTAssertEqual(t3?.hour, 0) + + let t4 = Time("schedule") + XCTAssertNil(t4) + + XCTAssertEqual(Time(hour: 1)!.intervalSinceZeroClock, 1.hour) } - func testPeriod() { - let p0 = (1.year + 2.months + 3.days).tidied(to: .day) - XCTAssertEqual(p0.years, 1) - XCTAssertEqual(p0.months, 2) - XCTAssertEqual(p0.days, 3) - - let p1 = Period("one second")?.tidied(to: .second) - XCTAssertNotNil(p1) - XCTAssertEqual(p1!.seconds, 1) - let p2 = Period("two hours and ten minutes")?.tidied(to: .day) - XCTAssertNotNil(p2) - XCTAssertEqual(p2!.hours, 2) - XCTAssertEqual(p2!.minutes, 10) - let p3 = Period("1 year, 2 months and 3 days")?.tidied(to: .day) - XCTAssertNotNil(p3) - XCTAssertEqual(p3!.years, 1) - XCTAssertEqual(p3!.months, 2) - XCTAssertEqual(p3!.days, 3) - - let date = Date(year: 1989, month: 6, day: 4) + 1.year - let year = date.dateComponents.year - XCTAssertEqual(year, 1990) - - let p4 = Period(hours: 25).tidied(to: .day) - XCTAssertEqual(p4.days, 1) + func testWeekday() { + XCTAssertEqual(Weekday.monday.toDateComponents().weekday!, 2) } static var allTests = [ ("testInterval", testInterval), + ("testMonthday", testMonthday), + ("testPeriod", testPeriod), ("testTime", testTime), - ("testPeriod", testPeriod) + ("testWeekday", testWeekday) ] }