我没有对此进行详尽的测试,但它基于我经常使用的一些类别方法。要确定 date1 和 date2 之间有多少个工作日(假设 date1 < date2),请将此函数的返回值除以 24*60*60(一天中的秒数)。
这将计算分为第一个周末之前的天数、最后一个周末之后的天数和中间几周的天数。周末从周六 00:00:00 开始,到周日 23:59:59 结束。通常,您希望避免假设一天有 24 小时,因为可能存在与夏令时相关的特殊情况。所以我建议在重要的时候使用 NSCalendar 来计算时间间隔。但这发生在周末,因此对于这种情况并不重要。
这里有两种方法。如果您提供开始日期和要延长到的工作日(工作日)数,则第一个返回 NSDate 结束日期。(如果工作日数为负数,则返回更早的日期。)第二个返回对应于两个给定 NSDate 日期之间的工作日数(包括小数天数)的秒数。
我试图将计算保持在一个时区内,但默认为系统时区。(顺便说一下,如果你想用小数天数计算,把weekdays
参数改成浮点数。或者你可能想用一个以秒为单位的参数计算。如果是这样,那么还要改变第一种方法中totalInterval的计算。你赢了'不必转换为秒。该方法中的所有后续计算都以秒为单位。)
- (NSDate*) calculateWeekDaysEndDateFrom:(NSDate*)_date1 and:(int)weekdays {
NSTimeInterval dayInterval = 24*60*60;
NSTimeInterval totalInterval = dayInterval * (float) weekdays;
NSTimeInterval secondsBeforeWeekend;
NSTimeInterval secondsAfterWeekend;
NSTimeInterval secondsInInterveningWeeks;
int numberOfWeeks;
NSDate *dateOfFirstSaturdayMorning;
NSDate *dateOfLastSundayNight;
NSDate *finalDate;
if (weekdays >0) {
dateOfFirstSaturdayMorning = [_date1 theFollowingWeekend];
secondsBeforeWeekend = [dateOfFirstSaturdayMorning timeIntervalSinceDate:_date1];
numberOfWeeks = (int)((totalInterval - secondsBeforeWeekend)/(5.0 * dayInterval));
secondsInInterveningWeeks = 5 * (float)(numberOfWeeks * dayInterval);
secondsAfterWeekend = totalInterval - secondsBeforeWeekend - secondsInInterveningWeeks;
dateOfLastSundayNight = [[dateOfFirstSaturdayMorning dateByAddingDays:7*numberOfWeeks+2] dateByAddingTimeInterval:-1]; // move from saturday morning to monday morning, then back off 1 second
finalDate = [dateOfLastSundayNight dateByAddingTimeInterval:secondsAfterWeekend];
}
else {
dateOfLastSundayNight = [_date1 thePreviousWeekend];
secondsAfterWeekend = [date1 timeIntervalSinceDate:dateOfLastSundayNight];
numberOfWeeks = (int)((-totalInterval - secondsAfterWeekend)/(5.0 * dayInterval));
secondsInInterveningWeeks = 5 * (float)(numberOfWeeks * dayInterval);
dateOfFirstSaturdayMorning = [[dateOfLastSundayNight dateByAddingDays:-(7*numberOfWeeks+2)] dateByAddingTimeInterval:+1];
secondsBeforeWeekend = -totalInterval - secondsInInterveningWeeks - secondsAfterWeekend;
finalDate = [dateOfFirstSaturdayMorning dateByAddingTimeInterval:-secondsBeforeWeekend];
}
NSLog(@"dateOfFirstSaturdayMorning = %@", [dateOfFirstSaturdayMorning descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(@"dateOfLastSundayNight = %@",[dateOfLastSundayNight descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(@"date 1 = %@", date1);
NSLog (@"daysBeforeWeekend = %.2f", secondsBeforeWeekend/((float)dayInterval));
NSLog (@"daysBetweenWeekends = %.2f", secondsInInterveningWeeks/((float)(dayInterval)));
NSLog (@"daysAfterWeekend = %.2f", secondsAfterWeekend/((float)dayInterval));
NSLog (@"numberOfWeekdays = %.2f", (secondsBeforeWeekend + secondsInInterveningWeeks + secondsAfterWeekend)/((float)dayInterval));
NSLog(@"endDateFromWeekdays = %@", [finalDate descriptionWithLocale:[NSLocale currentLocale]]);
return finalDate;
}
- (NSTimeInterval) calculateWeekdaysFrom:(NSDate*)_date1 and:(NSDate*)_date2 {
if (_date1 && _date2) {
NSTimeInterval secondsBeforeWeekend;
NSTimeInterval secondsAfterWeekend;
NSDate *dateOfFirstSaturdayMorning;
NSDate *dateOfLastSundayNight;
NSTimeInterval dayInterval = 24*60*60; // This isn't always true, e.g., if daylight savings intervenes. (But that happens on the weekend in most places.)
// see if they are in the same week
if (([_date1 ordinality] < [_date2 ordinality]) && [_date2 timeIntervalSinceDate:_date1] <= 5*dayInterval) {
return [_date2 timeIntervalSinceDate:_date1];
}
// time interval before a first weekend
if ([_date1 ordinality] == 1 || [_date1 ordinality] == 7) {
secondsBeforeWeekend = 0;
dateOfFirstSaturdayMorning = _date1; // This is just a convenience. It's not true. But, later, rounding takes place to deal with it.
}
else {
dateOfFirstSaturdayMorning = [_date1 theFollowingWeekend];
secondsBeforeWeekend = [dateOfFirstSaturdayMorning timeIntervalSinceDate:_date1];
}
int ordDate2 = [_date2 ordinality];
int ordFirstSaturday = [dateOfFirstSaturdayMorning ordinality];
// time interval after a last weekend
if ([_date2 ordinality] == 1 || [_date2 ordinality] == 7) {
secondsAfterWeekend = 0;
dateOfLastSundayNight = _date2; // Again, this is just a convenience. It's not true.
}
else {
dateOfLastSundayNight = [_date2 thePreviousWeekend];
secondsAfterWeekend = [_date2 timeIntervalSinceDate:dateOfLastSundayNight];
}
NSTimeInterval intervalBetweenWeekends = [dateOfLastSundayNight timeIntervalSinceDate:dateOfFirstSaturdayMorning];
int numberOfWeeks = (int) (intervalBetweenWeekends/(7*dayInterval));
int secondsInInterveningWeeks = (float) (5*dayInterval*numberOfWeeks);
NSLog(@"date 1 = %@", [_date1 descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(@"date 2 = %@", [_date2 descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(@"dateOfFirstSaturdayMorning = %@", [dateOfFirstSaturdayMorning descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(@"dateOfLastSundayNight = %@",[dateOfLastSundayNight descriptionWithLocale:[NSLocale currentLocale]]);
NSLog (@"daysBeforeWeekend = %.2f", secondsBeforeWeekend/((float)dayInterval));
NSLog (@"daysBetweenWeekends = %.2f", secondsInInterveningWeeks/((float)(dayInterval)));
NSLog (@"daysAfterWeekend = %.2f", secondsAfterWeekend/((float)dayInterval));
NSLog (@"numberOfWeekdays = %.2f", (secondsBeforeWeekend + secondsInInterveningWeeks + secondsAfterWeekend)/((float)dayInterval));
return secondsBeforeWeekend + secondsInInterveningWeeks + secondsAfterWeekend;
}
else
return 0;
}
NSDate 上的分类方法文件是 NSDate+help.h
@interface NSDate (help)
+ (NSDate *) LSExtendedDateWithNaturalLanguageString:(NSString *)dateString WithFormatter:(NSDateFormatter*)dateFormatter;
- (NSUInteger)ordinality;
- (NSDate*) theFollowingWeekend;
- (NSDate *) thePreviousWeekend;
- (NSDate *) dateByAddingDays:(NSInteger) numberOfDays;
- (NSDate *) dateByMovingToBeginningOfDayInTimeZone:(NSTimeZone*)tz;
- (NSDate *) dateByMovingToEndOfDayInTimeZone:(NSTimeZone*)tz;
@end
和 NSDate+help.m
#import "NSDate+help.h"
@implementation NSDate (help)
// thrown in for testing
+ (NSDate *) LSExtendedDateWithNaturalLanguageString:(NSString *)dateString WithFormatter:(NSDateFormatter*)dateFormatter{
[dateFormatter setDateFormat:@"yyyy-MM-dd HHmm"];
[dateFormatter setLocale:[NSLocale currentLocale]];
//NSDate *formattedDate = [dateFormatter dateFromString:@"2008-12-3T22-11-30-123"];
return [dateFormatter dateFromString:dateString];
}
- (NSUInteger)ordinality {
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:[NSTimeZone systemTimeZone]];
return [calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:self];
}
- (NSDate*) theFollowingWeekend {
NSUInteger myOrdinality = [self ordinality];
NSDate *dateOfFollowingWeekend = [self dateByAddingDays:(7-myOrdinality)%7];
return [dateOfFollowingWeekend dateByMovingToBeginningOfDayInTimeZone:(NSTimeZone*)nil];
}
- (NSDate *) thePreviousWeekend {
NSUInteger myOrdinality = [self ordinality];
NSDate *dateOfPreviousWeekend = [self dateByAddingDays:(1-myOrdinality)];
return [dateOfPreviousWeekend dateByMovingToEndOfDayInTimeZone:(NSTimeZone*)nil];
}
- (NSDate *) dateByAddingDays:(NSInteger) numberOfDays {
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
dayComponent.day = numberOfDays;
NSCalendar *theCalendar = [NSCalendar currentCalendar];
return [theCalendar dateByAddingComponents:dayComponent toDate:self options:0];
}
- (NSDate *) dateByMovingToBeginningOfDayInTimeZone:(NSTimeZone*)tz {
NSTimeZone *timezone;
if (tz)
timezone = tz;
else
timezone = [NSTimeZone systemTimeZone];
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents* parts = [[NSCalendar currentCalendar] components:flags fromDate:self];
[parts setHour:0];
[parts setMinute:0];
[parts setSecond:0];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timezone];
return [calendar dateFromComponents:parts];
}
- (NSDate *)dateByMovingToEndOfDayInTimeZone:(NSTimeZone*)tz {
NSTimeZone *timezone;
if (tz)
timezone = tz;
else
timezone = [NSTimeZone systemTimeZone];
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents* parts = [[NSCalendar currentCalendar] components:flags fromDate:self];
[parts setHour:23];
[parts setMinute:59];
[parts setSecond:59];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timezone];
return [calendar dateFromComponents:parts];
}
@end
category 方法ordinality
返回接收者星期几的数字。星期日 = 1,星期六 = 7。这用于找出第一周结束之前有多少天,以及上周开始之后有多少天。(计算实际上是在几秒钟内完成的。)
类别方法theFollowingWeekend
并thePreviousWeekend
返回接收者日期之后的星期六早上午夜的 NSDate 和接收者日期之后的星期日午夜前一秒的 NSDate。这些方法假设您已经验证接收方日期不是周末。我在主要方法中处理了这个问题。查找序数 == 1 或 7 的检查。
dateByMovingToBeginningOfDayInTimeZone:
并将dateByMovingToEndOfDayInTimeZone:
接收方日期的小时、分钟和秒分别设置为 00:00:00 和 23:59:59。这是为了划定周末,从时区的周六早上午夜到周日晚上的午夜。
希望这可以帮助。这是一个让我更加熟悉时间和日期功能的练习。
我要感谢 Keith Lazuka 和他的 iPhone 日历组件,因为这段代码的萌芽。
这是使用这些功能的测试程序用户界面的屏幕截图:

这是您的示例,运行第一种方法。感兴趣的项目被突出显示。
. 为此,我进行了简单的修改以接受小数天数(我在上面提到过,但没有包含在上面显示的代码中)