Add polyfill for Calender's `nextDate` method on linux

Remove autoupdatingCurrent, it's unavailable on linux
This commit is contained in:
Quentin 2018-08-27 12:50:54 +08:00
parent db5109b042
commit 5daabf3701
12 changed files with 173 additions and 42 deletions

View File

@ -21,6 +21,8 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
62AA63022133970A00A442A5 /* Calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA63012133970A00A442A5 /* Calendar.swift */; };
62AA63042133A0FB00A442A5 /* CalendarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA63032133A0FB00A442A5 /* CalendarTests.swift */; };
667D2DF72132C5390071DC89 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667D2DF62132C5390071DC89 /* DeinitObserver.swift */; };
667D2DF92132C95D0071DC89 /* DeinitObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */; };
OBJ_45 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; };
@ -69,6 +71,8 @@
/* Begin PBXFileReference section */
626D761B2113F9D100FCAFFE /* README.zh_cn.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.zh_cn.md; sourceTree = "<group>"; };
62AA63012133970A00A442A5 /* Calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calendar.swift; sourceTree = "<group>"; };
62AA63032133A0FB00A442A5 /* CalendarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarTests.swift; sourceTree = "<group>"; };
667D2DF62132C5390071DC89 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; };
667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserverTests.swift; sourceTree = "<group>"; };
668685ED210DCC0E009305C3 /* .swift-version */ = {isa = PBXFileReference; lastKnownFileType = text; path = ".swift-version"; sourceTree = "<group>"; };
@ -125,6 +129,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
62AA6300213396CE00A442A5 /* Platforms */ = {
isa = PBXGroup;
children = (
667D2DF62132C5390071DC89 /* DeinitObserver.swift */,
62AA63012133970A00A442A5 /* Calendar.swift */,
);
name = Platforms;
sourceTree = "<group>";
};
668685EC210DCBD4009305C3 /* Related */ = {
isa = PBXGroup;
children = (
@ -146,7 +159,6 @@
OBJ_10 /* Bucket.swift */,
OBJ_11 /* Extensions.swift */,
OBJ_13 /* Lock.swift */,
667D2DF62132C5390071DC89 /* DeinitObserver.swift */,
);
name = Utils;
sourceTree = "<group>";
@ -168,8 +180,9 @@
children = (
OBJ_26 /* AtomicTests.swift */,
OBJ_27 /* BucketTests.swift */,
OBJ_29 /* ExtensionsTests.swift */,
62AA63032133A0FB00A442A5 /* CalendarTests.swift */,
667D2DF82132C95D0071DC89 /* DeinitObserverTests.swift */,
OBJ_29 /* ExtensionsTests.swift */,
);
name = UtilsTests;
sourceTree = "<group>";
@ -235,6 +248,7 @@
OBJ_19 /* TaskHub.swift */,
OBJ_21 /* Timeline.swift */,
668685F5210DD226009305C3 /* DateTime */,
62AA6300213396CE00A442A5 /* Platforms */,
668685F4210DD21A009305C3 /* Utils */,
);
name = Schedule;
@ -345,6 +359,7 @@
OBJ_45 /* Atomic.swift in Sources */,
OBJ_46 /* Bucket.swift in Sources */,
OBJ_47 /* Extensions.swift in Sources */,
62AA63022133970A00A442A5 /* Calendar.swift in Sources */,
OBJ_48 /* Interval.swift in Sources */,
OBJ_49 /* Lock.swift in Sources */,
OBJ_50 /* Monthday.swift in Sources */,
@ -372,6 +387,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 0;
files = (
62AA63042133A0FB00A442A5 /* CalendarTests.swift in Sources */,
OBJ_77 /* AtomicTests.swift in Sources */,
OBJ_78 /* BucketTests.swift in Sources */,
OBJ_79 /* DateTimeTests.swift in Sources */,

View File

@ -0,0 +1,56 @@
//
// Calendar.swift
// Schedule
//
// Created by Quentin MED on 2018/8/27.
//
import Foundation
extension Calendar {
func next(_ weekday: Weekday, after date: Date = Date()) -> Date? {
let components = weekday.toDateComponents()
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
return nextDate(after: date, matching: components, matchingPolicy: .strict)
#elseif os(Linux)
var c = dateComponents(in: .current, from: date)
var days = components.weekday! - c.weekday!
if days <= 0 {
days += 7
}
return self.date(byAdding: .day, value: days, to: date)?.zeroClock()
#endif
}
func next(_ monthday: Monthday, after date: Date = Date()) -> Date? {
let components = monthday.toDateComponents()
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
return nextDate(after: date, matching: components, matchingPolicy: .strict)
#elseif os(Linux)
var old = dateComponents(in: .current, from: date)
var new = DateComponents(calendar: self, timeZone: .current)
new.year = old.year
new.month = components.month
new.day = components.day
if components.month! < old.month! {
new.year! += 1
} else if components.month! == old.month! {
if components.day! <= old.day! {
new.year! += 1
}
}
return self.date(from: new)
#endif
}
}

View File

@ -36,7 +36,7 @@ extension Date {
func zeroClock() -> Date {
let calendar = Calendar.gregorian
let timeZone = TimeZone.autoupdatingCurrent
let timeZone = TimeZone.current
var dateComponents = calendar.dateComponents(in: timeZone, from: self)
dateComponents.hour = 0
dateComponents.minute = 0

View File

@ -35,7 +35,7 @@ public enum Monthday {
case december(Int)
var isToday: Bool {
let lhs = Calendar.gregorian.dateComponents(in: TimeZone.autoupdatingCurrent, from: Date())
let lhs = Calendar.gregorian.dateComponents(in: TimeZone.current, from: Date())
let rhs = toDateComponents()
return lhs.month == rhs.month && lhs.day == rhs.day
}
@ -57,7 +57,7 @@ public enum Monthday {
case .december(let n): month = 12; day = n
}
return DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent,
timeZone: TimeZone.current,
month: month,
day: day)
}

View File

@ -386,13 +386,12 @@ extension Schedule {
public static func every(_ weekday: Weekday) -> DateMiddleware {
let schedule = Schedule.make { () -> AnyIterator<Date> in
let calendar = Calendar.gregorian
let components = weekday.toDateComponents()
var date: Date!
return AnyIterator<Date> {
if weekday.isToday {
date = Date().zeroClock()
} else if date == nil {
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict)
date = calendar.next(weekday, after: Date())
} else {
date = calendar.date(byAdding: .day, value: 7, to: date)
}
@ -414,16 +413,15 @@ extension Schedule {
}
/// Creates a schedule that executes the task every specific day in the month.
public static func every(_ monthDay: Monthday) -> DateMiddleware {
public static func every(_ monthday: Monthday) -> DateMiddleware {
let schedule = Schedule.make { () -> AnyIterator<Date> in
let calendar = Calendar.gregorian
let components = monthDay.toDateComponents()
var date: Date!
return AnyIterator<Date> {
if monthDay.isToday {
if monthday.isToday {
date = Date().zeroClock()
} else if date == nil {
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict)
date = calendar.next(monthday, after: Date())
} else {
date = calendar.date(byAdding: .year, value: 1, to: date)
}

View File

@ -93,12 +93,12 @@ public struct Time {
formatter = DateFormatter()
formatter?.locale = Locale(identifier: "en_US_POSIX")
formatter?.calendar = Calendar.gregorian
formatter?.timeZone = TimeZone.autoupdatingCurrent
formatter?.timeZone = TimeZone.current
formatter.dateFormat = fmt
}
if let date = formatter.date(from: string) {
Time.FormatterCache.setObject(formatter, forKey: NSString(string: fmt))
let components = Calendar.gregorian.dateComponents(in: TimeZone.autoupdatingCurrent, from: date)
let components = Calendar.gregorian.dateComponents(in: TimeZone.current, from: date)
if let hour = components.hour,
let minute = components.minute,
let second = components.second,
@ -119,7 +119,7 @@ public struct Time {
func toDateComponents() -> DateComponents {
return DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent,
timeZone: TimeZone.current,
hour: hour, minute: minute,
second: second, nanosecond: nanosecond)
}

View File

@ -14,12 +14,12 @@ public enum Weekday: Int {
var isToday: Bool {
return Calendar.gregorian
.dateComponents(in: .autoupdatingCurrent, from: Date()).weekday == rawValue
.dateComponents(in: .current, from: Date()).weekday == rawValue
}
func toDateComponents() -> DateComponents {
return DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent,
timeZone: TimeZone.current,
weekday: rawValue)
}
}

View File

@ -0,0 +1,49 @@
//
// CalendarTests.swift
// ScheduleTests
//
// Created by Quentin MED on 2018/8/27.
//
import XCTest
@testable import Schedule
final class CalendarTests: XCTestCase {
func testNextWeekday() {
let date = Date(year: 2018, month: 8, day: 1, hour: 6)
let n0 = Calendar.gregorian.next(.monday, after: date)
XCTAssertEqual(n0?.dateComponents.year, 2018)
XCTAssertEqual(n0?.dateComponents.month, 8)
XCTAssertEqual(n0?.dateComponents.day, 6)
XCTAssertEqual(n0?.dateComponents.weekday, 2)
let n1 = Calendar.gregorian.next(.friday, after: date)
XCTAssertEqual(n1?.dateComponents.year, 2018)
XCTAssertEqual(n1?.dateComponents.month, 8)
XCTAssertEqual(n1?.dateComponents.day, 3)
XCTAssertEqual(n1?.dateComponents.weekday, 6)
}
func testNextMonthday() {
let date = Date(year: 2000, month: 4, day: 1, hour: 6)
let n0 = Calendar.gregorian.next(.april(1), after: date)
XCTAssertEqual(n0?.dateComponents.year, 2001)
XCTAssertEqual(n0?.dateComponents.month, 4)
XCTAssertEqual(n0?.dateComponents.day, 1)
let n1 = Calendar.gregorian.next(.october(10), after: date)
XCTAssertEqual(n1?.dateComponents.year, 2000)
XCTAssertEqual(n1?.dateComponents.month, 10)
XCTAssertEqual(n1?.dateComponents.day, 10)
}
static var allTests = [
("testNextWeekday", testNextWeekday),
("testNextMonthday", testNextMonthday)
]
}

View File

@ -8,6 +8,8 @@
import XCTest
@testable import Schedule
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
final class DeinitObserverTests: XCTestCase {
func testObserver() {
@ -26,3 +28,5 @@ final class DeinitObserverTests: XCTestCase {
("testObserver", testObserver)
]
}
#endif

View File

@ -11,19 +11,19 @@ import Foundation
extension Date {
var dateComponents: DateComponents {
return Calendar.gregorian.dateComponents(in: TimeZone.autoupdatingCurrent, from: self)
return Calendar.gregorian.dateComponents(in: TimeZone.current, from: self)
}
var localizedDescription: String {
let formatter = DateFormatter()
formatter.timeZone = TimeZone.autoupdatingCurrent
formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return formatter.string(from: self)
}
init(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0, nanosecond: Int = 0) {
let components = DateComponents(calendar: Calendar.gregorian,
timeZone: TimeZone.autoupdatingCurrent,
timeZone: TimeZone.current,
year: year, month: month, day: day,
hour: hour, minute: minute, second: second,
nanosecond: nanosecond)

View File

@ -11,13 +11,13 @@ import XCTest
final class TaskTests: XCTestCase {
func testSchedule() {
let expectation = XCTestExpectation(description: "testSchedule")
let e = expectation(description: "testSchedule")
let date = Date()
let task = Schedule.after(0.5.second).do {
XCTAssertTrue(Date().timeIntervalSince(date).isAlmostEqual(to: 0.5, leeway: 0.1))
expectation.fulfill()
e.fulfill()
}
wait(for: [expectation], timeout: 2)
waitForExpectations(timeout: 2)
task.cancel()
}
@ -46,15 +46,15 @@ final class TaskTests: XCTestCase {
}
func testAddAndRemoveActions() {
let expectation = XCTestExpectation(description: "testAddAndRemoveActions")
let e = expectation(description: "testAddAndRemoveActions")
let task = Schedule.after(0.5.second).do { }
let date = Date()
let key = task.addAction { _ in
XCTAssertTrue(Date().timeIntervalSince(date).isAlmostEqual(to: 0.5, leeway: 0.1))
expectation.fulfill()
e.fulfill()
}
XCTAssertEqual(task.countOfActions, 2)
wait(for: [expectation], timeout: 2)
waitForExpectations(timeout: 2)
task.removeAction(byKey: key)
XCTAssertEqual(task.countOfActions, 1)
task.cancel()
@ -65,25 +65,28 @@ final class TaskTests: XCTestCase {
func testAddAndRemoveTags() {
let task = Schedule.never.do { }
task.addTag("c")
task.addTags("n", "s", "z")
XCTAssertTrue(task.tags.contains("c"))
XCTAssertTrue(task.tags.contains("z"))
task.removeTag("c")
XCTAssertFalse(task.tags.contains("c"))
task.removeTags("s", "z")
XCTAssertFalse(task.tags.contains("s"))
XCTAssertFalse(task.tags.contains("z"))
let tagA = UUID().uuidString
let tagB = UUID().uuidString
let tagC = UUID().uuidString
task.addTag(tagA)
task.addTags(tagB, tagC)
XCTAssertTrue(task.tags.contains(tagA))
XCTAssertTrue(task.tags.contains(tagC))
task.removeTag(tagA)
XCTAssertFalse(task.tags.contains(tagA))
task.removeTags(tagB, tagC)
XCTAssertFalse(task.tags.contains(tagB))
XCTAssertFalse(task.tags.contains(tagC))
task.cancel()
}
func testReschedule() {
let expectation = XCTestExpectation(description: "testReschedule")
let e = expectation(description: "testReschedule")
var i = 0
let task = Schedule.after(0.1.second).do { (task) in
i += 1
if task.countOfExecution == 6 && task.timeline.estimatedNextExecution == nil {
expectation.fulfill()
e.fulfill()
}
if task.countOfExecution > 6 {
XCTFail("should never come here")
@ -92,11 +95,12 @@ final class TaskTests: XCTestCase {
DispatchQueue.global().async(after: 0.5.second) {
task.reschedule(Schedule.every(0.1.second).first(5))
}
wait(for: [expectation], timeout: 2)
waitForExpectations(timeout: 2)
task.cancel()
}
func testParasiticTask() {
let e = expectation(description: "testParasiticTask")
let fn = {
let obj = NSObject()
Schedule.after(0.5.second).do(host: obj, onElapse: {
@ -104,11 +108,14 @@ final class TaskTests: XCTestCase {
})
}
fn()
wait(for: [], timeout: 1)
DispatchQueue.main.async(after: 0.75.seconds) {
e.fulfill()
}
waitForExpectations(timeout: 1)
}
func testLifetime() {
let expectation = XCTestExpectation(description: "testLifetime")
let e = expectation(description: "testLifetime")
let task = Schedule.after(1.hour).do { }
task.setLifetime(1.second)
XCTAssertEqual(task.lifetime, 1.second)
@ -122,9 +129,9 @@ final class TaskTests: XCTestCase {
}
DispatchQueue.global().async(after: 2.second) {
XCTAssertTrue(task.isCancelled)
expectation.fulfill()
e.fulfill()
}
wait(for: [expectation], timeout: 5)
waitForExpectations(timeout: 5)
}
static var allTests = [

View File

@ -1,6 +1,6 @@
import XCTest
#if !os(macOS)
#if os(Linux)
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(DateTimeTests.allTests),
@ -9,6 +9,7 @@ public func allTests() -> [XCTestCaseEntry] {
testCase(TaskTests.allTests),
testCase(AtomicTests.allTests),
testCase(BucketTests.allTests),
testCase(CalendarTests.allTests),
testCase(ExtensionsTests.allTests)
]
}