Last active
July 16, 2019 14:06
-
-
Save preble/b08b6d9ee161534e6c1f to your computer and use it in GitHub Desktop.
Experimenting with creating a SequenceType for iterating over a range of dates. Blogged here: http://adampreble.net/blog/2014/09/iterating-over-range-of-dates-swift/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
func > (left: NSDate, right: NSDate) -> Bool { | |
return left.compare(right) == .OrderedDescending | |
} | |
extension NSCalendar { | |
func dateRange(startDate startDate: NSDate, endDate: NSDate, stepUnits: NSCalendarUnit, stepValue: Int) -> DateRange { | |
let dateRange = DateRange(calendar: self, startDate: startDate, endDate: endDate, stepUnits: stepUnits, stepValue: stepValue, multiplier: 0) | |
return dateRange | |
} | |
} | |
struct DateRange : SequenceType { | |
var calendar: NSCalendar | |
var startDate: NSDate | |
var endDate: NSDate | |
var stepUnits: NSCalendarUnit | |
var stepValue: Int | |
private var multiplier: Int | |
func generate() -> Generator { | |
return Generator(range: self) | |
} | |
struct Generator: GeneratorType { | |
var range: DateRange | |
mutating func next() -> NSDate? { | |
guard let nextDate = range.calendar.dateByAddingUnit(range.stepUnits, | |
value: range.stepValue * range.multiplier, | |
toDate: range.startDate, | |
options: []) else { | |
return nil | |
} | |
if nextDate > range.endDate { | |
return nil | |
} | |
else { | |
range.multiplier += 1 | |
return nextDate | |
} | |
} | |
} | |
} | |
// Usage: | |
func testDateRange() { | |
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! | |
let startDate = NSDate(timeIntervalSinceNow: 0) | |
let endDate = NSDate(timeIntervalSinceNow: 24*60*60*7-1) | |
let dateRange = calendar.dateRange(startDate: startDate, | |
endDate: endDate, | |
stepUnits: .Day, | |
stepValue: 1) | |
let datesInRange = Array(dateRange) | |
XCTAssertEqual(datesInRange.count, 7, "Expected 7 days") | |
XCTAssertEqual(datesInRange.first, startDate, "First date should have been the start date.") | |
} |
For Swift 4:
extension Calendar {
func dateRange(startDate: Date, endDate: Date, component: Calendar.Component, step: Int) -> DateRange {
let dateRange = DateRange(calendar: self, startDate: startDate, endDate: endDate, component: component, step: step, multiplier: 0)
return dateRange
}
}
struct DateRange : Sequence, IteratorProtocol {
var calendar: Calendar
var startDate: Date
var endDate: Date
var component: Calendar.Component
var step: Int
var multiplier: Int
mutating func next() -> Date? {
guard let nextDate = calendar.date(byAdding: component, value: step * multiplier, to: startDate)
else {
return nil
}
if nextDate > endDate {
return nil
} else {
multiplier += 1
return nextDate
}
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Nidhee you probably don't need this still, but I updated for Swift 3 today :)