如何从NSDate中减去持续时间,但不包括周末?

Dav*_*vid 6 cocoa objective-c nsdate nsdatecomponents

以今天为例,我如何确定230个工作日之前的日期?

我知道如何使用while循环检查日期迭代地执行它,如果它是工作日则减去1,但我想知道是否有更好的方法.

另外,我们以周日下午1点为例,减去3个工作日和2个小时.首先,从周末减去工作时间是没有意义的.因此,它必须将时间移至星期五的23:59:59,然后减去3天和2小时.

如果它是星期一凌晨1:30,我将减去5天和3个工作小时,那么结果应该是前一周的星期五晚上22:30.


用于测试Kevin方法的代码:

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *dc = [[NSDateComponents new] autorelease];
dc.month = 12;
dc.day = 19;
dc.year = 2011;
dc.hour = 1;
dc.minute = 0;
dc.second = 0;

NSDate *date = [cal dateFromComponents:dc];

NSLog(@"%@", [date descriptionWithCalendarFormat:nil timeZone:nil locale:nil]);
date = dateBySubtractingWorkOffset(date, 0, 2);
NSLog(@"%@", [date descriptionWithCalendarFormat:nil timeZone:nil locale:nil]);
Run Code Online (Sandbox Code Playgroud)

输出日志:

2011-12-02 16:33:46.878 otest[7124:707] 2011-12-19 01:00:00 -0500
2011-12-02 16:33:47.659 otest[7124:707] 2011-12-18 23:00:00 -0500
Run Code Online (Sandbox Code Playgroud)

它永远不应该是12-18,因为那是一个星期天.

Lil*_*ard 4

计算出您的日期距上周末还有多长时间,然后从您的日期和偏移量中减去该金额。现在,您可以将偏移量除以 5 来计算偏移量中有多少个整周,然后将其乘以 7 并从日期中减去这个新值。取之前的偏移量(除以 5 的偏移量)并除以 5,即可得到剩余天数。如果它大于 0,请从您的日期中减去该偏移量 + 2(周末)。

请注意,这假设每个工作日都是工作日。公司假期往往会使这一假设无效。如果你需要处理假期,你就会遇到更棘手的问题。

更新:这里尝试修复大卫的代码以实际表达这里的想法:

NSDate *dateBySubtractingWorkOffset(NSDate *date, NSUInteger days, NSUInteger hours) {
    const int secsInHour = 60*60;
    const int secsInDay = 24*secsInHour;
    NSTimeInterval offset = days*secsInDay + hours*secsInHour;
    NSCalendar *cal = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];

    // figure out distance from last weekend
    {
        NSUInteger units = NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit;
        NSDateComponents *dc = [cal components:units fromDate:date];
        if (dc.weekday == 1 || dc.weekday == 7) {
            // we're in the weekend already. Let's just back up until friday
            // and then we can start our calculations there
        } else {
            // figure out our offset from sunday 23:59:59
            dc.day -= (dc.weekday - 1);
            dc.weekday = 1;
            dc.hour = 23;
            dc.minute = 23;
            dc.second = 23;
            NSDate *sunday = [cal dateFromComponents:dc];
            NSTimeInterval newOffset = [date timeIntervalSinceDate:sunday];
            if (offset < newOffset) {
                // our offset doesn't even go back to sunday, we don't need any calculations
                return [date dateByAddingTimeInterval:-offset];
            }
            offset -= [date timeIntervalSinceDate:sunday];
            // Now we can jump back to Friday with our new offset
        }
        // Calculate last friday at 23:59:59
        dc.day -= (dc.weekday % 7 + 1);
        dc.hour = 23;
        dc.minute = 59;
        dc.second = 59;
        date = [cal dateFromComponents:dc];
    }

    // We're now set to Friday 23:59:59
    // Lets figure out how many weeks we have
    int secsInWorkWeek = 5*secsInDay;
    NSInteger weeks = (NSInteger)trunc(offset / secsInWorkWeek);
    offset -= weeks*secsInWorkWeek;
    if (weeks > 0) {
        // subtract that many weeks from the date
        NSDateComponents *dc = [[NSDateComponents alloc] init];
        dc.week = -weeks;
        date = [cal dateByAddingComponents:dc toDate:date options:0];
        [dc release];
    }
    // now we can just subtract our remaining offset from the date
    return [date dateByAddingTimeInterval:-offset];
}
Run Code Online (Sandbox Code Playgroud)