Skip to content

Commit 95999f8

Browse files
Wilsonator5000TheCodedSelf
authored andcommitted
Add ability to change timezone for calculating next run date (TheCodedSelf#28)
* Add ability to change timezone for calculating next run date * Add unit tests for timezone implementation * Discuss usage of timezone implementation in README * Fix typo in README
1 parent d293dc4 commit 95999f8

File tree

4 files changed

+58
-4
lines changed

4 files changed

+58
-4
lines changed

Example/SwiftCronExample.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
959A1B451D944A2700E685B3 /* MonthFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 959A1B441D944A2700E685B3 /* MonthFieldTests.swift */; };
2525
959A1B481D944CE400E685B3 /* DateBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 959A1B471D944CE400E685B3 /* DateBuilder.swift */; };
2626
959A1B4D1D94518B00E685B3 /* TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 959A1B4C1D94518B00E685B3 /* TestData.swift */; };
27+
C8B4BFA221A067E800222752 /* TimeZoneTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B4BFA121A067E800222752 /* TimeZoneTests.swift */; };
2728
E048D8BD8613C0CAF3BB4824 /* Pods_SwiftCronExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB8E7C90A033C373325A9FC6 /* Pods_SwiftCronExample.framework */; };
2829
FDCC4D322535804B48C02ECA /* Pods_SwiftCronExampleTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A585942BE5510532D56F9F1 /* Pods_SwiftCronExampleTests.framework */; };
2930
/* End PBXBuildFile section */
@@ -68,6 +69,7 @@
6869
9A585942BE5510532D56F9F1 /* Pods_SwiftCronExampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftCronExampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6970
9FC712B82D2D075E149E73AB /* Pods-SwiftCronExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftCronExampleTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftCronExampleTests/Pods-SwiftCronExampleTests.release.xcconfig"; sourceTree = "<group>"; };
7071
A8CB7B09FFB21F86177ED344 /* Pods-SwiftCronExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftCronExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwiftCronExample/Pods-SwiftCronExample.release.xcconfig"; sourceTree = "<group>"; };
72+
C8B4BFA121A067E800222752 /* TimeZoneTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeZoneTests.swift; sourceTree = "<group>"; };
7173
EB8E7C90A033C373325A9FC6 /* Pods_SwiftCronExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftCronExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7274
/* End PBXFileReference section */
7375

@@ -163,6 +165,7 @@
163165
8679D2A61CE1F5130071D783 /* SwiftCronExampleTests-Bridging-Header.h */,
164166
8679D2B71CE1FA760071D783 /* DescriptionTests.swift */,
165167
9529D3571DD37D91005ACCE0 /* CronExpressionTests.swift */,
168+
C8B4BFA121A067E800222752 /* TimeZoneTests.swift */,
166169
);
167170
path = SwiftCronExampleTests;
168171
sourceTree = "<group>";
@@ -401,6 +404,7 @@
401404
959A1B4D1D94518B00E685B3 /* TestData.swift in Sources */,
402405
8679D2B21CE1F51F0071D783 /* MonthTests.swift in Sources */,
403406
8679D2B51CE1F51F0071D783 /* WeekdayTests.swift in Sources */,
407+
C8B4BFA221A067E800222752 /* TimeZoneTests.swift in Sources */,
404408
8679D2B11CE1F51F0071D783 /* MinuteTests.swift in Sources */,
405409
9529D3581DD37D91005ACCE0 /* CronExpressionTests.swift in Sources */,
406410
8679D2B31CE1F51F0071D783 /* SwiftCronTests.swift in Sources */,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import XCTest
2+
import SwiftCron
3+
4+
class TimeZoneTests: XCTestCase {
5+
6+
override func setUp() {
7+
super.setUp()
8+
// Put setup code here. This method is called before the invocation of each test method in the class.
9+
}
10+
11+
override func tearDown() {
12+
// Put teardown code here. This method is called after the invocation of each test method in the class.
13+
super.tearDown()
14+
}
15+
16+
func testTimeZoneDifferencesResultInVariationOfNextRunDate() {
17+
let newYorkTZ = TimeZone(identifier: "America/New_York")!
18+
let losAngelesTZ = TimeZone(identifier: "America/Los_Angeles")!
19+
20+
let cronExpression = CronExpression(cronString: "0 10 12 9 * *")!
21+
22+
let nextRunNewYorkTZ = cronExpression.getNextRunDateFromNow(adjustingForTimeZone: newYorkTZ)!
23+
let nextRunLosAngelesTZ = cronExpression.getNextRunDateFromNow(adjustingForTimeZone: losAngelesTZ)!
24+
25+
XCTAssertNotEqual(nextRunNewYorkTZ, nextRunLosAngelesTZ)
26+
}
27+
}

README.md

+14
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ let dateToStartSearchOn = NSDate()
8484
let nextRunDate = myCronExpression.getNextRunDate(dateToStartSearchOn)
8585
```
8686

87+
##### Custom timezones
88+
89+
By default SwiftCron uses the system's current timezone to calculate the next run date. To override this behavior, just pass the timezone you want into `CronExpression.getNextRunDateFromNow(adjustingForTimeZone:)` or `CronExpression.getNextRunDate(_:adjustingForTimeZone:)` methods.
90+
91+
```swift
92+
// Every 12th September at 10 AM
93+
let cronExpression = CronExpression(cronString: "0 10 12 9 * *")
94+
95+
// Calculate the next run date as if the cron expression was created in the
96+
// America/Los_Angeles timezone
97+
let losAngeles = TimeZone(identifier: "America/Los_Angeles")!
98+
let nextRun = cronExpression.getNextRunDateFromNow(adjustingForTimeZone: losAngeles)
99+
```
100+
87101
## Contributing
88102

89103
- Pull requests for bug fixes and new features are most welcome.

Sources/CronExpression.swift

+13-4
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,22 @@ public class CronExpression {
6262

6363
// MARK: - Get Next Run Date
6464

65-
public func getNextRunDateFromNow() -> Date? {
66-
return getNextRunDate(Date())
65+
public func getNextRunDateFromNow(adjustingForTimeZone outputTimeZone: TimeZone = .current) -> Date? {
66+
return getNextRunDate(Date(), adjustingForTimeZone: outputTimeZone)
6767
}
6868

69-
public func getNextRunDate(_ date: Date) -> Date? {
70-
return getNextRunDate(date, skip: 0)
69+
public func getNextRunDate(_ date: Date, adjustingForTimeZone outputTimeZone: TimeZone = .current) -> Date? {
70+
guard let nextRun = getNextRunDate(date, skip: 0) else { return nil }
71+
return adjust(date: nextRun, for: outputTimeZone)
7172
}
73+
74+
func adjust(date: Date, for timeZone: TimeZone) -> Date {
75+
let currentTZOffset = TimeZone.current.secondsFromGMT(for: date)
76+
let outputTZOffset = timeZone.secondsFromGMT(for: date)
77+
78+
let tzDifference = TimeInterval(currentTZOffset - outputTZOffset)
79+
return date.addingTimeInterval(tzDifference)
80+
}
7281

7382
func getNextRunDate(_ date: Date, skip: Int) -> Date? {
7483
guard matchIsTheoreticallyPossible(date) else {

0 commit comments

Comments
 (0)