それはあなたのように見え、将来であると仮定して、後方にfromDate
あります。toDate
targetDate
これが私がこの問題を解決する方法です。
カテゴリを作成して、NSCalendar
2つの日付の間に特定の平日が発生する回数をカウントします。
@interface NSCalendar (AlexCategory)
// The number of times weekday number `weekday` (1 = Sunday, 7 = Saturday)
// occurs between `fromDate` and `toDate`. If `fromDate` falls on the desired
// weekday, it is counted. If `toDate` falls on the desired weekday, it is NOT counted.
- (NSInteger)countOfWeekday:(NSInteger)weekday fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate;
@end
この新しいメソッドを実装するために、次の年、月、日、および曜日を取得することから始めますfromDate
。
@implementation NSCalendar (AlexCategory)
- (NSInteger)countOfWeekday:(NSInteger)weekday fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate {
NSDateComponents *components = [self components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSWeekdayCalendarUnit fromDate:fromDate];
fromDate
次に、次の希望する平日までの日数をカウントします。
NSInteger daysUntilDesiredWeekday = weekday - components.weekday;
// If fromDate is a Wednesday and weekday is Monday (for example),
// daysUntilDesiredWeekday is negative. Fix that.
NSRange weekdayRange = [self minimumRangeOfUnit:NSWeekdayCalendarUnit];
if (daysUntilDesiredWeekday < weekdayRange.location) {
daysUntilDesiredWeekday += weekdayRange.length;
}
希望の平日に当たるdaysUntilDesiredWeekday
とゼロになることに注意してください。fromDate
NSDateComponents
これで、次の日以降の最初の希望する平日を表すを作成できますfromDate
。
NSDateComponents *firstDesiredWeekday = [[NSDateComponents alloc] init];
firstDesiredWeekday.year = components.year;
firstDesiredWeekday.month = components.month;
firstDesiredWeekday.day = components.day + daysUntilDesiredWeekday;
最初に希望する平日に更新fromDate
し、それ以降の場合は0を返しますtoDate
。
fromDate = [self dateFromComponents:firstDesiredWeekday];
if ([fromDate compare:toDate] != NSOrderedAscending) {
return 0;
}
次に、更新fromDate
からtoDate
:までのすべての日(希望する平日だけでなく)をカウントします。
NSInteger allDaysCount = [self components:NSDayCalendarUnit
fromDate:fromDate toDate:toDate options:0].day;
これを1週間の日数で割って、希望する平日の数だけを数えることができます。希望の平日からカウントを開始したため、週の余りには希望の平日も含まれるため、切り上げる必要があります。
// Adding weekdayRange.length - 1 makes the integer division round up.
return (allDaysCount + weekdayRange.length - 1) / weekdayRange.length;
}
@end
次のようにメソッドをテストできます。
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = 2012;
components.month = 1;
components.day = 1;
NSDate *fromDate = [calendar dateFromComponents:components];
for (NSUInteger i = 1; i <= 365; ++i) {
components.day = i;
NSDate *toDate = [calendar dateFromComponents:components];
NSLog(@"%@ to %@: %ld Mondays", fromDate, toDate, [calendar countOfWeekday:2 fromDate:fromDate toDate:toDate]);
}
出力は次のように始まります。
2012-08-06 16:27:05.751 tuesdays[81152:403] 0 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-01 06:00:00 +0000
2012-08-06 16:27:05.754 tuesdays[81152:403] 0 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-02 06:00:00 +0000
2012-08-06 16:27:05.756 tuesdays[81152:403] 1 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-03 06:00:00 +0000
2012-08-06 16:27:05.758 tuesdays[81152:403] 1 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-04 06:00:00 +0000
2012-08-06 16:27:05.759 tuesdays[81152:403] 1 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-05 06:00:00 +0000
2012-08-06 16:27:05.760 tuesdays[81152:403] 1 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-06 06:00:00 +0000
2012-08-06 16:27:05.762 tuesdays[81152:403] 1 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-07 06:00:00 +0000
2012-08-06 16:27:05.763 tuesdays[81152:403] 1 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-08 06:00:00 +0000
2012-08-06 16:27:05.763 tuesdays[81152:403] 1 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-09 06:00:00 +0000
2012-08-06 16:27:05.764 tuesdays[81152:403] 2 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-10 06:00:00 +0000
2012-08-06 16:27:05.765 tuesdays[81152:403] 2 Mondays from 2012-01-01 06:00:00 +0000 to 2012-01-11 06:00:00 +0000
これは、次の出力によれば正しいように見えますcal 1 2012
。
January 2012
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
(toDate
はカウントされないため、2012-1-2は月曜日ですが、2012-1-1から2012-1-2までの月曜日は0です。)