Я не тестировал это полностью, но он основан на некоторых методах категорий, которые я регулярно использую. Чтобы определить, сколько дней недели находится между date1 и date2 (предполагается, что date1
При этом вычисление делится на количество дней до первых выходных, количество дней после последних выходных и количество дней в прошедших неделях. Выходные начинаются в субботу в 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
Метод категории ordinality
возвращает номер дня недели получателя. Воскресенье = 1, суббота = 7. Используется, чтобы узнать, сколько дней осталось до конца первой недели и сколько дней осталось после начала последней недели. (Расчеты фактически выполняются в секундах.)
Методы категорий theFollowingWeekend
и thePreviousWeekend
возвращают NSDate в полночь субботнего утра, следующего за датой получателя, и NSDate за одну секунду до полуночи воскресенья, следующего за датой получателя. Эти методы предполагают, что вы уже подтвердили, что дата получателя не в выходные. Я справился с этим в основных методах. Ищите проверки ординальности == 1 или 7.
dateByMovingToBeginningOfDayInTimeZone:
и dateByMovingToEndOfDayInTimeZone:
устанавливают часы, минуты и секунды даты получателя на 00:00:00 и 23:59:59 соответственно. Это для разграничения выходных, которые продолжаются с полуночи субботнего утра до полуночи воскресного вечера в часовом поясе.
Надеюсь, это поможет. Это было упражнение для меня, чтобы лучше познакомиться с функциями времени и даты.
Я кредитую Кита Лазука и его компонент календаря для iPhone за прорастание этого кода.
Вот снимок экрана пользовательского интерфейса тестовой программы, который использует эти функции:
Вот ваш пример, запустите первый метод. Интересующие предметы выделены.
. Для этого я сделал простую модификацию, чтобы принимать дробные дни (которые я упоминал выше, но не включал в код, показанный выше)