4

I'd like to increment over days in a loop, so that it counts 2012-11-10, 2012-11-11, 2012-11-12, …

What's the most performant way to achieve this?

NSDate *iterationDate = [NSDate date];

for (int i = 0; i < 100; i++) {
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setYear:0];
    [comps setMonth:0];
    [comps setWeek:0];
    [comps setDay:1];
    [comps setHour:0];
    [comps setMinute:0];
    [comps setSecond:0];
    iterationDate = [currentCalendar dateByAddingComponents:comps toDate:iterationDate options:0];
}
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
swalkner
  • 16,679
  • 31
  • 123
  • 210

2 Answers2

3

If you need all the intermediate NSDates, just pull comps out of the loop:

NSDate *iterationDate = [NSDate date];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setYear:0];
[comps setMonth:0];
[comps setWeek:0];
[comps setDay:1];
[comps setHour:0];
[comps setMinute:0];
[comps setSecond:0];

for (int i = 0; i < 100; i++) {
    iterationDate = [currentCalendar dateByAddingComponents:comps toDate:iterationDate options:0];
}

You can achieve something similar using CoreFoundation APIs:

CFCalendarRef calendar = CFCalendarCreateWithIdentifier(0, kCFGregorianCalendar);
CFAbsoluteTime at = CFAbsoluteTimeGetCurrent();
const CFOptionFlags options = 0;
for (int i = 0; i < NIter; ++i) {
    if (0 == CFCalendarAddComponents(calendar, &at, options, "d", 1)) {
        assert(0 && "uh-oh");
    }
    CFDateRef date = CFDateCreate(0, at);
    // store result
    CFRelease(date);
}
CFRelease(calendar);

That measured out to be 33% faster than Foundation. It's even faster if you don't need to create the CFDates and can simply store the CFAbsoluteTime values.

justin
  • 104,054
  • 14
  • 179
  • 226
  • okay - and then it's the most performant way? so nothing faster than `dateByAddingComponents`? – swalkner Nov 10 '12 at 09:38
  • @swalkner yeah - CF was the fastest i tried, assuming you need all the niceties the APIs you use are needed (e.g. calendar, locale, timezone, etc.). – justin Nov 10 '12 at 10:44
  • I don't need the CFDate, but a NSTimeInterval; is it possible to get it from CFAbsoluteTime? – swalkner Nov 10 '12 at 19:42
  • @swalkner yes. `CFAbsoluteTime`, like `NSTimeInterval`, represents seconds using a `double`. so you just need to decide where to subtract to get the result you want. – justin Nov 10 '12 at 20:01
2

I think that your way is not bad, just do not allocate each time the NSDateComponents object (comps):

NSDate *iterationDate = [NSDate date];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:1];
for (int i = 0; i < 100; i++) 
    iterationDate = [currentCalendar dateByAddingComponents:comps toDate:iterationDate options:0];

Another way could be (this should be faster, but needs to be tested ...):

NSDate *iterationDate = [NSDate date];
int daysToAdd = 1;  
for (int i = 0; i < 100; i++)
       iterationDate = [iterationDate addTimeInterval:60*60*24*daysToAdd];
aleroot
  • 71,077
  • 30
  • 176
  • 213
  • 3
    The second way won't work if it goes over a day where there is a transition between daylight savings – Abizern Nov 10 '12 at 08:55