我看了一遍,没有找到一个函数可以给出两个日期之间的工作日数。
我该怎么做?
当然它不会计算假期,因为每个国家都有自己的假期,但我可以为我的国家做一些特定的功能。
编辑:我没有问如何获得两个日期之间的天数。
我看了一遍,没有找到一个函数可以给出两个日期之间的工作日数。
我该怎么做?
当然它不会计算假期,因为每个国家都有自己的假期,但我可以为我的国家做一些特定的功能。
编辑:我没有问如何获得两个日期之间的天数。
所以你对工作日的定义似乎是周一到周五。我们称这些为“工作日”,而不是“周末”(即周六和周日)。
如何计算从 astartDate
到a 的工作日数并不明显endDate
,所以让我们从一个更简单的计算开始:从startDate
到的周数endDate
。“周”是指从星期日开始的 7 天时间段。
如果startDate
和endDate
落在同一周,我们将计算该周一次。如果它们落在不同的周内,我们将计算包含startDate
的周、包含 的endDate
周以及其间的任何周。所以这是一个“包含”的周数。
给定这个周数,我们显然可以乘以 5 来得到一周中的工作日数。但这不一定是从startDate
到的工作日数endDate
。如果startDate
是星期日或星期一,并且endDate
是星期六或星期日,那么它是正确的,否则它会计算一些应该排除的天数。
应该排除多少天?首先,考虑startDate
. 我们需要排除startDate
一周之前的工作日数(0 表示周日和周一,1 表示周二,2 表示周三,3 表示周四,4 表示周五,5 表示周六)。然后考虑endDate
。我们需要排除endDate
一周中接下来的工作日数,如果是工作日,我们需要排除endDate
自身(周日和周一为 5,周二为 4,周三为 3,周四为 2,周五为 1,0星期六)。
考虑到这一切,从startDate
到的工作日数的公式endDate
是
5 * inclusive count of weeks from startDate to endDate
- count of workdays preceding startDate
- count of workdays following and including endDate
startDate
例如,如果是星期一并且endDate
是同一周的星期二,这将给出 1 。如果在这种情况下你宁愿得到 2(换句话说,endDate
如果不是周末,你想算作工作日之一),那么公式是
5 * inclusive count of weeks from startDate to endDate
- count of workdays preceding startDate
- count of workdays following (but not including) endDate
现在让我们使用 Cocoa 出色的日历计算支持在 Objective-C 中编写它。我们将在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
to的天数endDate
并除以 7:
/** 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];
}
以下是我们如何推进到周日:
- (NSDate *)Rob_sundayAfterDate:(NSDate *)date {
return [self Rob_dateByAddingDays:8 - [self Rob_weekdayOfDate:date]
toDate:date];
}
以下是我们如何计算日期的工作日(从 1=Sunday 到 7=Saturday):
- (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
为负数,该方法将及时回滚日期。
要计算某个日期之前的工作日数,我们可以使用蛮力(因为一周中只有 7 天需要考虑),这很容易理解,也可以使用公式,它更小但更难理解:
- (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);
}
计算日期后的工作日数是类似的,但我们需要两个版本,具体取决于我们是包括还是不包括给定日期:
- (NSInteger)Rob_countOfWorkdaysFollowingAndIncludingDate:(NSDate *)date {
switch ([self Rob_weekdayOfDate:date]) {
case 1: return 5;
case 2: return 5;
case 3: return 4;
case 4: return 3;
case 5: return 2;
case 6: return 1;
case 7: return 0;
default: abort();
}
// equivalently: return MIN(7 - [self Rob_weekdayOfDate:date], 5);
}
- (NSInteger)Rob_countOfWorkdaysFollowingDate:(NSDate *)date {
switch ([self Rob_weekdayOfDate:date]) {
case 1: return 5;
case 2: return 4;
case 3: return 3;
case 4: return 2;
case 5: return 1;
case 6: return 0;
case 7: return 0;
default: abort();
}
// equivalently: return MAX(6 - [self Rob_weekdayOfDate:date], 0);
}
@end