3

Getting the number of days in a month is easy enough,

    NSDate *today = startDate; 
    NSCalendar *c = [NSCalendar currentCalendar];
    NSRange days = [c rangeOfUnit:NSDayCalendarUnit
                           inUnit:NSMonthCalendarUnit
                          forDate:today];

But how do I get the number of working days (Monday - Fridays) in the current month?

LpLrich
  • 2,463
  • 2
  • 20
  • 31
  • Do you want to consider holidays, or just count the number of days Mon-Fri? – Krumelur Feb 25 '14 at 13:21
  • Just the mon-fridays, I don't mind if it's a holiday or not. – LpLrich Feb 25 '14 at 13:22
  • It's not possible with NSCalendar. – Mani Feb 25 '14 at 13:25
  • I had thought there might be an elegant solution that involves counting the amount of NSWeekdayCalendarUnits between 2 & 6 in a given month.. – LpLrich Feb 25 '14 at 13:30
  • This could be useful: http://stackoverflow.com/a/21945364/1187415. – Martin R Feb 25 '14 at 13:30
  • Thanks for the link, it's a pretty meaty one though so I'll get my dentures out before trying it in my code. There is also an answer below which I'll have to look at but cheers, Martin, for the link. – LpLrich Feb 25 '14 at 13:36

4 Answers4

5

Unfortunately there's no direct way to loop between two dates and you can't get the weekday directly from a NSDate object. So you need to add just a few more lines to make it work. The key here is to use NSDateComponents. For this example, I'm using the Gregorian calendar. By default, according to Apple's documentation, weekdays start on Sunday, which is day one (literally 1). Please, don't consider that Sunday is zero (it's common to get confused).

It may require some syntax changes as i have not tried it on XCode

NSInteger count = 0;
NSInteger sunday = 1;
NSInteger saturday = 7;

// Set the incremental interval for each interaction.
NSDateComponents *oneDay = [[NSDateComponents alloc] init];
[oneDay setDay:1];

// Using a Gregorian calendar.
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDate *currentDate = fromDate;

// Iterate from fromDate until toDate
while ([currentDate compare:toDate] == NSOrderedAscending) {

    NSDateComponents *dateComponents = [calendar components:NSWeekdayCalendarUnit fromDate:currentDate];

    if (dateComponents.weekday != saturday && dateComponents.weekday != sunday) {
        count++;
    }

    // "Increment" currentDate by one day.
    currentDate = [calendar dateByAddingComponents:oneDay
                                            toDate:currentDate
                                           options:0];
}

NSLog(@"count = %d", count);
Ravi_Parmar
  • 12,319
  • 3
  • 25
  • 36
4

Let's see… If we had 28 days in each month, then we would always have 4 sat+sun's, i.e. 8 off-days. But there are months with 29,30,31 days. Analyze them possibilities:

1   . 28  | 29  30  31
Mon . Sun | Mon Tue Wed
Tue . Mon | Tue Wed Thu
Wed . Tue | Wed Thu Fri
Thu . Wed | Thu Fri SAT
Fri . Thu | Fri SAT SUN
Sat . Fri | SAT SUN Mon
Sun . Sat | SUN Mon Tue

     29 30 31
4 -> +0 +0 +1
5 -> +0 +1 +1
6 -> +1 +1 +0
7 -> +1 +0 +0

#define inRange(x, a, b) ((x) >= (a) && (x) <= (b))

int nDays = <get days in month>;
int firstWeekday = <get first weekday index>;
int nOffDays = 8;

if (nDays > 28 && inRange(firstWeekday, 6, 7)) nOffDays += 1;
if (nDays > 29 && inRange(firstWeekday, 5, 6)) nOffDays += 1;
if (nDays > 30 && inRange(firstWeekday, 4, 5)) nOffDays += 1;

int nWorkDays = nDays - nOffDays;
user3125367
  • 2,920
  • 1
  • 17
  • 17
1

If you want to, you can solve this problem with NSCalendar without any looping. You could accomplish this by dividing up the problem

Basic idea

const NSUInteger Sunday = 1, ....., Saturday = 7;

NSUInteger workdaysCount 
        = getNumberOfDaysInMonth(today)
        - getWeekdayCountInMonth(today, Saturday)
        - getWeekdayCountInMonth(today, Sunday);

Now, we have two functions to implement:

Get the number of days in a month

You already got it right,

NSUInteger getNumberOfDaysInMonth(NSDate* date) {
    NSCalendar *c = [NSCalendar currentCalendar];
    return [c rangeOfUnit:NSCalendarUnitDay
                   inUnit:NSMonthCalendarUnit
                  forDate:date].length;
}

Get the count of a given weekday in a month

This is the trickier one, but NSCalendar provides the basic building blocks:

NSUInteger getWeekdayCountInMonth(NSDate* date, enum Weekdays weekday) {
    NSCalendar *c = [NSCalendar currentCalendar];
    NSDate* startOfMonth = getMonthStart(date);
    NSDate* firstMatchingWeekday = [c dateBySettingUnit:NSWeekdayCalendarUnit value:weekday ofDate:startOfMonth options:0];

    // Number of days from start of month until we are at given weekday
    NSUInteger daysToWeekday = [c components:NSDayCalendarUnit
                                    fromDate:startOfMonth
                                      toDate:firstMatchingWeekday options:0].day;

    NSUInteger days = getNumberOfDaysInMonth(date) - daysToWeekday;

    return (days + 6) / 7;
}
Krumelur
  • 31,081
  • 7
  • 77
  • 119
  • Hmm this might be the best solution so far. I don't know which answer to mark as correct! I'm leaving it as the one that gave me a correct answer first. – LpLrich Feb 25 '14 at 14:20
0

You're almost there: you've already got the number of days (nDays) in a given month; now loop from 1 to nDays, for each day find its day of week, and if it's between Monday and Friday, increment some counter. Tada, you've got what you wanted.

Cyrille
  • 25,014
  • 12
  • 67
  • 90