From f607cb03c5e6eac9cc378d579e1fcd888a530dd6 Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Thu, 9 Jun 2016 15:01:35 -0400 Subject: [PATCH] (fix) handle flattened timezone definitions (fixes #2690) --- SOPE/NGCards/iCalTimeZone.m | 54 ++++++++++++++++++++----------- SOPE/NGCards/iCalTimeZonePeriod.m | 42 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/SOPE/NGCards/iCalTimeZone.m b/SOPE/NGCards/iCalTimeZone.m index 25239f016..0499dd9fb 100644 --- a/SOPE/NGCards/iCalTimeZone.m +++ b/SOPE/NGCards/iCalTimeZone.m @@ -183,29 +183,44 @@ static NSArray *knownTimeZones; return [[self uniqueChildWithTag: @"tzid"] flattenedValuesForKey: @""]; } -- (NSCalendarDate *) _occurrenceForPeriodNamed: (NSString *) pName +- (NSDictionary *) _occurrenceForPeriodNamed: (NSString *) pName forDate: (NSCalendarDate *) aDate { NSArray *periods; NSEnumerator *periodsList; iCalTimeZonePeriod *period; NSCalendarDate *occurrence; + NSDictionary *periodDict; occurrence = nil; + periodDict = nil; periods = [[self childrenWithTag: pName] sortedArrayUsingSelector: @selector (compare:)]; periodsList = [periods reverseObjectEnumerator]; - while (!occurrence - && (period = (iCalTimeZonePeriod *) [periodsList nextObject])) - occurrence = [period occurrenceForDate: aDate]; + while ((period = (iCalTimeZonePeriod *) [periodsList nextObject])) + { + if ([periods count] > 1 && ([[period startDate] yearOfCommonEra] > [aDate yearOfCommonEra])) + continue; - return occurrence; + occurrence = [period occurrenceForDate: aDate]; + + if (occurrence && (!periodDict || [occurrence earlierDate: [periodDict objectForKey: @"occurrence"]] == [periodDict objectForKey: @"occurrence"])) + { + periodDict = [NSDictionary dictionaryWithObjectsAndKeys: + period, @"period", + occurrence, @"occurrence",nil]; + occurrence = nil; + } + } + + return periodDict; } - (iCalTimeZonePeriod *) periodForDate: (NSCalendarDate *) date { - NSCalendarDate *daylightOccurence, *standardOccurence; + //NSCalendarDate *daylightOccurence, *standardOccurence; + NSDictionary *daylightOccurence, *standardOccurence; iCalTimeZonePeriod *period; /* FIXME, this could cause crashes when timezones are not properly @@ -213,30 +228,33 @@ static NSArray *knownTimeZones; daylightOccurence = [self _occurrenceForPeriodNamed: @"daylight" forDate: date]; + standardOccurence = [self _occurrenceForPeriodNamed: @"standard" forDate: date]; - if (!standardOccurence) + if (!standardOccurence && !daylightOccurence) + period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"standard"]; + else if (!standardOccurence) period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"daylight"]; else if (!daylightOccurence) period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"standard"]; - else if ([date earlierDate: daylightOccurence] == date) + else if ([date earlierDate: [daylightOccurence objectForKey: @"occurrence"]] == date) { - if ([date earlierDate: standardOccurence] == date - && ([standardOccurence earlierDate: daylightOccurence] - == standardOccurence)) - period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"daylight"]; + if ([date earlierDate: [standardOccurence objectForKey: @"occurrence"]] == date + && ([[standardOccurence objectForKey: @"occurrence"] earlierDate: [daylightOccurence objectForKey: @"occurrence"]] + == [standardOccurence objectForKey: @"occurrence"])) + period = (iCalTimeZonePeriod *) [daylightOccurence objectForKey: @"period"]; else - period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"standard"]; + period = (iCalTimeZonePeriod *) [standardOccurence objectForKey: @"period"]; } else { - if ([standardOccurence earlierDate: date] == standardOccurence - && ([daylightOccurence earlierDate: standardOccurence] - == daylightOccurence)) - period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"standard"]; + if ([[standardOccurence objectForKey: @"occurrence"] earlierDate: date] == [standardOccurence objectForKey: @"occurrence"] + && ([[daylightOccurence objectForKey: @"occurrence"] earlierDate: [standardOccurence objectForKey: @"occurrence"]] + == [daylightOccurence objectForKey: @"occurrence"] )) + period = (iCalTimeZonePeriod *) [standardOccurence objectForKey: @"period"]; else - period = (iCalTimeZonePeriod *) [self uniqueChildWithTag: @"daylight"]; + period = (iCalTimeZonePeriod *) [daylightOccurence objectForKey: @"period"]; } return period; diff --git a/SOPE/NGCards/iCalTimeZonePeriod.m b/SOPE/NGCards/iCalTimeZonePeriod.m index b31e30e82..d21dfe111 100644 --- a/SOPE/NGCards/iCalTimeZonePeriod.m +++ b/SOPE/NGCards/iCalTimeZonePeriod.m @@ -37,6 +37,8 @@ if ([classTag isEqualToString: @"RRULE"]) tagClass = [iCalRecurrenceRule class]; + else if ([classTag isEqualToString: @"RDATE"]) + tagClass = [iCalDateTime class]; else if ([classTag isEqualToString: @"DTSTART"]) tagClass = [iCalDateTime class]; else if ([classTag isEqualToString: @"TZOFFSETFROM"] @@ -247,19 +249,59 @@ return tmpDate; } +- (NSCalendarDate *) _occurrenceFromRdate: (NSCalendarDate *) refDate + rDates: (NSArray *) rDatesIn; +{ + NSArray *rDates; + NSEnumerator *dateList; + NSCalendarDate *rDateCur, *rDateOut; + NSString *dateString; + unsigned i; + + rDateCur = nil; + rDateOut = nil; + + dateList = [rDatesIn objectEnumerator]; + + while ((dateString = [dateList nextObject])) + { + rDates = [(iCalDateTime*) dateString dateTimes]; + + for (i = 0; i < [rDates count]; i++) + { + rDateCur = [rDates objectAtIndex: i]; + if (!rDateOut || ([rDateCur yearOfCommonEra] > [rDateOut yearOfCommonEra] && [refDate yearOfCommonEra] >= [rDateCur yearOfCommonEra])) + rDateOut = rDateCur; + } + } + + return rDateOut; +} + + - (NSCalendarDate *) occurrenceForDate: (NSCalendarDate *) refDate; { NSCalendarDate *tmpDate; iCalRecurrenceRule *rrule; + NSArray *rDates; tmpDate = nil; rrule = (iCalRecurrenceRule *) [self uniqueChildWithTag: @"rrule"]; + rDates = (NSArray *) [self childrenWithTag: @"rdate"]; + + if ([rDates count]) + { + tmpDate = [self _occurrenceFromRdate: refDate rDates: rDates]; + return tmpDate; + } if ([rrule isVoid]) tmpDate = [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"] dateTime]; else if ([rrule untilDate] == nil || [refDate compare: [rrule untilDate]] == NSOrderedAscending) tmpDate = [self _occurrenceForDate: refDate byRRule: rrule]; + else if ([refDate compare: [rrule untilDate]] == NSOrderedDescending) + tmpDate = [rrule untilDate]; return tmpDate; }