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

Using today as an example, how do I determine which date it was, 230 workdays ago?

I know how to do it iteratively with a while loop checking date and subtracting 1 if it's a workday, but I'm wondering if there is a better method.

Also, let's take a Sunday 1 PM as an example, and subtract 3 work days and 2 hours from that time. First, it doesn't make sense to subtract work-time from weekends. So it would have to move the time to 23:59:59 of Friday, and then subtract those 3 days and 2 hours.

If it's a Monday at 1:30 AM, and I'm subtracting 5 days and 3 work-hours from that time, then the result should be Friday 22:30 PM of the previous week.

Code to test Kevin's method:

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]);

Output log:

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

It should never be 12-18, since that's a Sunday.

解决方案

Figure out how long from the last weekend your date is, subtract that amount from both your date and your offset. Now you can divide your offset by 5 to figure out how many full weeks are in your offset, and then multiply that by 7 and subtract this new value from your date. Take your previous offset (the one you divided by 5) and mod it by 5, to get the number of remaining days. If it's greater than 0, subtract that offset + 2 (for the weekend) from your date.

Note, this assumes every single weekday is a workday. Corporate holidays tend to make that assumption invalid. If you need to handle holidays, you're in for a much tougher problem.

Update: Here's an attempt to fix David's code to actually express the idea here:

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];

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值