Reputation: 21870
I'm trying to determine payment schedules when there are 24 payments per year (i.e. twice per month). The payments aren't going to always be 1st and 15th. I have to start with a given day, and then calculate twice monthly payments starting from there.
NSCalendar and NSDateComponents only have the ability to add integer days, weeks, months, etc. What I really need to do is be able to add 0.5 months, but it only handles integers. I can't just add 15 days, because months have different numbers of days (which is part of the magic that NSCalendar is supposed to handle for us). The first payment of the month should be on the say day per month, so if the payments begin on the 8th, then every odd payment should be on the 8th and every even payment should be on the 8th + half a month. That's where complication comes in. I figure at the very least I can use month addition to get all of the odd numbered payments. Getting the date for those even numbered payments is the difficult part.
Is there a way to add half a month via the NSCalendar that I'm missing? If not, do you have an elegant solution for generating these 24 dates per year?
Upvotes: 1
Views: 992
Reputation: 11713
No, you have to create the half-month calculation yourself. Just for fun I wrote the following for you. Enjoy.
- (NSArray *)createPaymentScheduleWithStartDate:(NSDate *)startDate numberOfPayments:(NSUInteger)payments {
NSUInteger count = 0;
NSCalendar *cal = [NSCalendar currentCalendar];
NSMutableArray *schedule = [NSMutableArray arrayWithCapacity:payments];
// break down the start date for our future payments
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
| NSTimeZoneCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit;
NSDateComponents *comps = [cal components:unitFlags fromDate:startDate];
NSInteger month = [comps month];
NSInteger day = [comps day];
NSInteger year = [comps year];
// For simplicity sake, adjust day larger than 28
// this way we don't have to test each time we create a date
// if it valid.
if (day > 28)
day = 28;
// NSDate *a = startDate;
// because we want all payments to have the same time
// create the first again
NSDate *a = [cal dateFromComponents:comps];
do {
month++;
if (month > 12) {
year++;
month = 1;
}
// create the next month payment date
comps.month = month;
comps.day = day;
comps.year = year;
NSDate *b = [cal dateFromComponents:comps];
// find the middle date
NSTimeInterval m = [b timeIntervalSinceDate:a];
NSDate *c = [a dateByAddingTimeInterval:m / 2];
// add dates to our schedule array
[schedule addObject:a];
[schedule addObject:c];
count += 2;
// adjust to next group of payments
a = b;
} while (count < (payments + 5));
// because we add two payments at a time,
// we have to adjust that extra payment
if ([schedule count] > payments) {
// create range of our excess payments
NSRange range = NSMakeRange(payments, [schedule count] - payments);
[schedule removeObjectsInRange:range];
}
NSLog(@"Schedule: %@", schedule);
return [NSArray arrayWithArray:schedule];
}
Upvotes: 2
Reputation: 135548
Well, if you can calculate a date that is 1 month from the start date, calculating half a month is only one division away:
NSTimeInterval oneMonthInterval = [oneMonthLater timeIntervalSinceDate:startDate];
NSTimeInterval halfMonthInterval = oneMonthInterval / 2.0;
NSDate *halfAMonthLater = [startDate dateByAddingTimeInterval:halfMonthInterval];
Upvotes: 2