Here’s a general approach:
Step-by-step (in Swift):
1. Create an EKEvent with a recurrence rule.
2. Use EKEventStore to fetch events in a date range, which will include the recurring ones.
Example in Swift:
import EventKit
let store = EKEventStore()
store.requestAccess(to: .event) { (granted, error) in
if granted {
let calendars = store.calendars(for: .event)
let startDate = Date()
let endDate = Calendar.current.date(byAdding: .year, value: 1, to: startDate)!
let predicate = store.predicateForEvents(withStart: startDate, end: endDate, calendars: calendars)
let events = store.events(matching: predicate)
let recurringEvents = events.filter { $0.recurrenceRules != nil }
for event in recurringEvents {
print("Event:
@interface EKRecurrenceRule ()
-(NSArray<NSDate*>*)occurrencesForEventStart:(NSDate*)eventStart rangeStart:(NSDate*)rangeStart rangeEnd:(NSDate*)rangeEnd;
@end
@implementation EKRecurrenceRule ()
-(NSArray<NSDate*>*)occurrencesForEventStart:(NSDate*)eventStart rangeStart:(NSDate*)rangeStart rangeEnd:(NSDate*)rangeEnd
{
static NSCalendar *calendar = [NSCalendar.alloc initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
// daily every N days
// weekly every N weeks (on days)
// monthly every N months (on days of the month)
// monthly every N months (on the 1st/2nd/3rd/4th/5th/last day/weekday/weekend day/sun-mon-tue etc of the month)
// yearly every N years (in Jan/Feb/Mar etc) (on the 1st/2nd/3rd/4th/5th/last day/weekday/weekend day/sun-mon-tue etc of the month)
// always fix H:M:S
NSCalendarUnit hms = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
// make an array of NSDateComponents's for eg 1st and 3rd Sunday in the month
NSMutableArray<NSDateComponents*> *componentss = NSMutableArray.array;
switch( self.frequency )
{
case EKRecurrenceFrequencyDaily:
[componentss addObject:[calendar components:hms fromDate:eventStart]];
break;
case EKRecurrenceFrequencyWeekly:
if( self.daysOfTheWeek )
for( EKRecurrenceDayOfWeek *dow in self.daysOfTheWeek )
{
NSDateComponents *dc = [calendar components:hms fromDate:eventStart];
dc.weekday = dow.dayOfTheWeek;
[componentss addObject:dc];
}
else
[componentss addObject:[calendar components:hms|NSCalendarUnitWeekday fromDate:eventStart]];
break;
case EKRecurrenceFrequencyMonthly:
if( self.daysOfTheMonth )
{
for( NSNUmber *day in self.daysOfTheMonth )
{
NSDateComponents *dc = [calendar components:hms|NSCalendarUnitDay fromDate:eventStart];
dc.day = day.integerValue;
[componentss addObject:dc];
}
}
else if( self.daysOfTheWeek )
{
for( EKRecurrenceDayOfWeek *dow in self.daysOfTheWeek )
{
NSDateComponents *dc = [calendar components:hms|NSCalendarUnitWeekday fromDate:eventStart];
dc.weekday = dow.dayOfTheWeek;
dc.weekdayOrdinal = dow.weekNumber;
[componentss addObject:dc];
}];
}
else
[componentss addObject:[calendar components:hms|NSCalendarUnitDay fromDate:eventStart]];
break;
case EKRecurrenceFrequencyYearly:
[componentss addObject:[calendar components:hms|NSCalendarUnitDay|NSCalendarUnitMonth fromDate:eventStart]];
break;
}
NSMutableArray<NSDate*> *occurrences = NSMutableArray.array;
__block NSInteger interval = 1;
// enumerateDates seems to be buggy depending on this date, eg setting it to 1 second prior to the eventStart skips the 2nd expected occurrence
// I set it to eventStart - 1 day + 1 second
NSDate *afterDate = [NSDate dateWithTimeInterval:-86400+1 sinceDate:eventStart];
for( NSDateComponents *components in componentss )
{
[calendar enumerateDatesStartingAfterDate:afterDate
matchingComponents:components
options:NSCalendarMatchStrictly
usingBlock:^(NSDate *date, BOOL, BOOL *stop)
{
// reached end?
if( [date compare:rangeEnd]==NSOrderedDescending
|| (self.recurrenceEnd && [date compare:self.recurrenceEnd.endDate]==NSOrderedDescending) )
{
*stop = YES;
return;
}
// interval?
if( interval==1 )
{
// include this one, but only if after rangeStart
if( [rangeStart compare:date]==NSOrderedAscending )
[occurrences addObject:date];
// start counting down the interval
interval = self.interval;
}
else
{
// don't include this one, and count down the interval
interval--;
}
}];
}];
// sort dates and limit by number of occurrences
[occurrences sortUsingSelector:@selector(compare:)];
if( self.recurrenceEnd.occurrenceCount )
[occurrences removeObjectsInRange:NSMakeRange(self.recurrenceEnd.occurrenceCount,
occurrences.count-self.recurrenceEnd.occurrenceCount)];
return occurrences;
}
@end
这就是我想到的。它并不完整(一些比较深奥的月份/年份规则需要改进),但它是一个开始,对于比较简单的日历数据来说已经足够了。