我全神贯注地找不到一个函数,它给出了两个日期之间的工作日数。
我该怎么做?
当然不会算假期,因为每个国家都有自己的假期,但我可以为我的国家做一些特定的职能。
编辑:我没有问过如何获得两个日期之间的天数。答案 0 :(得分:3)
因此,您对工作日的定义似乎是星期一到星期五。我们称之为“工作日”,而不是“周末”(周六和周日)。
如何计算从startDate
到endDate
的工作日数量并不明显,所以让我们从一个更简单的计算开始:从startDate
到{{的周数1}}。 “周”是指从星期日开始的七天。
如果endDate
和startDate
属于同一周,我们会计算一周。如果它们在不同的星期内出现,我们会计算包含endDate
的周,包含startDate
的一周,以及介于两者之间的任何周。所以这是一个“包容性”的数周。
考虑到这个周数,我们显然可以乘以5来获得本周的工作日数。但这不一定是从endDate
到startDate
的工作日数。如果endDate
是星期日或星期一,而startDate
是星期六或星期日,那么它是正确的,但除非它计算应排除的几天。
应该排除多少天?首先,考虑endDate
。我们需要排除一周中startDate
之前的工作日数(周日和周一为0,周二为1,周三为2,周四为3,周五为4,周六为5)。然后考虑startDate
。我们需要排除本周endDate
之后的工作日数量,如果是工作日,我们需要排除endDate
本身(星期日和星期一为5,星期二为4,星期三为3,周四为2,周五为1,周六为0。
鉴于这一切,endDate
到startDate
的工作日数公式为
endDate
例如,如果 5 * inclusive count of weeks from startDate to endDate
- count of workdays preceding startDate
- count of workdays following and including endDate
是星期一,而startDate
是同一周的星期二,则会给出1。如果您希望在这种情况下获得2(换句话说,如果不是周末,您希望将endDate
计为工作日之一),则公式为
endDate
现在让我们使用Cocoa的优秀日历计算支持在Objective-C中编写它。我们将在 5 * inclusive count of weeks from startDate to endDate
- count of workdays preceding startDate
- count of workdays following (but not including) endDate
上实施此类别:
NSCalendar
要实现这些方法,我们将公式直接转换为代码:
@interface NSCalendar (Rob_Workdays)
/** I return the number of non-weekend days from startDate to endDate,
including the day containing `startDate` and excluding the day
containing `endDate`. */
- (NSInteger)Rob_countOfWorkdaysFromDate:(NSDate *)startDate
toDate:(NSDate *)endDate;
/** I return the number of non-weekend days from startDate to endDate,
including the day containing `startDate` and the day containing
`endDate`. */
- (NSInteger)Rob_countOfWorkdaysFromDate:(NSDate *)startDate
toAndIncludingDate:(NSDate *)endDate;
@end
当然,我们需要实现辅助方法,从周数开始。为了计算周数,我们向后滚动@implementation NSCalendar (Rob_Workdays)
- (NSInteger)Rob_countOfWorkdaysFromDate:(NSDate *)startDate toDate:(NSDate *)endDate {
return 5 * [self Rob_inclusiveCountOfWeeksFromDate:startDate toDate:endDate]
- [self Rob_countOfWorkdaysPrecedingDate:startDate]
- [self Rob_countOfWorkdaysFollowingAndIncludingDate:endDate];
}
- (NSInteger)Rob_countOfWorkdaysFromDate:(NSDate *)startDate toAndIncludingDate:(NSDate *)endDate {
return 5 * [self Rob_inclusiveCountOfWeeksFromDate:startDate toDate:endDate]
- [self Rob_countOfWorkdaysPrecedingDate:startDate]
- [self Rob_countOfWorkdaysFollowingDate:endDate];
}
直到它是星期日,我们向前滚动startDate
直到它是星期天。然后我们计算从endDate
到startDate
并除以7的天数:
endDate
以下是我们如何回到周日:
/** I return the number of weeks from `startDate` to `endDate`, including
the weeks containing each date (but only counting the week once if the
same week includes both dates). */
- (NSInteger)Rob_inclusiveCountOfWeeksFromDate:(NSDate *)startDate
toDate:(NSDate *)endDate
{
startDate = [self Rob_sundayOnOrBeforeDate:startDate];
endDate = [self Rob_sundayAfterDate:endDate];
NSDateComponents *components = [self components:NSCalendarUnitDay
fromDate:startDate toDate:endDate options:0];
return components.day / 7;
}
以下是我们如何前进到星期天:
- (NSDate *)Rob_sundayOnOrBeforeDate:(NSDate *)date {
return [self Rob_dateByAddingDays:1 - [self Rob_weekdayOfDate:date]
toDate:date];
}
以下是我们如何计算某个日期的工作日(从1 =星期日到7 =星期六):
- (NSDate *)Rob_sundayAfterDate:(NSDate *)date {
return [self Rob_dateByAddingDays:8 - [self Rob_weekdayOfDate:date]
toDate:date];
}
以下是我们如何在日期中添加一些天数:
- (NSInteger)Rob_weekdayOfDate:(NSDate *)date {
return [self components:NSCalendarUnitWeekday fromDate:date].weekday;
}
请注意,如果- (NSDate *)Rob_dateByAddingDays:(NSInteger)days toDate:(NSDate *)date {
if (days != 0) {
NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = days;
date = [self dateByAddingComponents:components toDate:date
options:0];
}
return date;
}
为负数,则该方法会将日期回滚。
要计算一个日期之前的工作日数,我们可以使用蛮力(因为一周只有七天要考虑),这很容易理解,或者我们可以使用一个较小的公式更难理解:
days
计算日期之后的工作日数量是类似的,但我们需要两个版本,具体取决于我们是否包含或排除给定日期:
- (NSInteger)Rob_countOfWorkdaysPrecedingDate:(NSDate *)date {
switch ([self Rob_weekdayOfDate:date]) {
case 1: return 0;
case 2: return 0;
case 3: return 1;
case 4: return 2;
case 5: return 3;
case 6: return 4;
case 7: return 5;
default: abort();
}
// equivalently: return MIN(MAX(0, [self Rob_weekdayOfDate:date] - 2), 5);
}