Skip to content

Instantly share code, notes, and snippets.

@preble
Last active July 16, 2019 14:06
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save preble/b08b6d9ee161534e6c1f to your computer and use it in GitHub Desktop.
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/
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.")
}
@FrancisBaileyH
Copy link

The one issue I've found with this, is it seems to skip over the first date you pass to it. I.e. Date 1 (exclusive) to Date N (inclusive)

@alexeyismirnov
Copy link

Yes, the start index is non-inclusive. I guess one needs to implement constructor that will subtract 1 day from start index.

@preble
Copy link
Author

preble commented Aug 25, 2016

Thanks for the comments pointing out the issue with the first date! I've updated the gist to fix that issue, as well as changed it to be created using an extension on NSCalendar, which I think is more idiomatic for NSCalendar-related date functions.

@leeprobert
Copy link

I'm guessing it's not that easy to create an option to iterate backwards through the range?

@Nidhee
Copy link

Nidhee commented Feb 8, 2017

I move from swift 2.2 to swift 3.0. Which is getting lot of errors. can you help in converting to swift 3.0

@aaronfalls
Copy link

@Nidhee you probably don't need this still, but I updated for Swift 3 today :)

@phatmann
Copy link

phatmann commented Dec 7, 2017

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