Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Experimenting with creating a SequenceType for iterating over a range of dates. Blogged here:
import Foundation
func > (left: NSDate, right: NSDate) -> Bool {
return == .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.")

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)

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


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.

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

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

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

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 = 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