diff --git a/Ical.Net.Tests/Calendars/Recurrence/RecurrenceTestCases.txt b/Ical.Net.Tests/Calendars/Recurrence/RecurrenceTestCases.txt index af6a5f73..69cb296a 100644 --- a/Ical.Net.Tests/Calendars/Recurrence/RecurrenceTestCases.txt +++ b/Ical.Net.Tests/Calendars/Recurrence/RecurrenceTestCases.txt @@ -6,3 +6,24 @@ INSTANCES:20241024,20241028,20241031 # Illegal rule part with multiple '=' RRULE:FREQ=WEEKLY;BYDAY=MO=;COUNT=3 EXCEPTION:System.ArgumentException + +# The first date belongs to 2009, so after 6 years it will be 2015, which has a week 53. +RRULE:FREQ=YEARLY;BYWEEKNO=53;BYDAY=TU,SA;INTERVAL=6;UNTIL=20170101T000000Z +DTSTART:20100102T000000 +INSTANCES:20100102T000000,20151229T000000,20160102T000000 + +# The first date belongs to 2009, so after 5 years it will be 2014, which has NO week 53. +RRULE:FREQ=YEARLY;BYWEEKNO=53;BYDAY=TU,SA;INTERVAL=5;UNTIL=20170101T000000Z +DTSTART:20100102T000000 +INSTANCES:20100102T000000 + +# The first date belongs to 2009, so after 7 years it will be 2014, which has NO week 53. +RRULE:FREQ=YEARLY;BYWEEKNO=53;BYDAY=TU,SA;INTERVAL=7;UNTIL=20170101T000000Z +DTSTART:20100102T000000 +INSTANCES:20100102T000000 + +# DTSTART is in 2024 but the week belongs to 2025, so after 3 years it will be 2028. +RRULE:FREQ=YEARLY;BYWEEKNO=1;BYDAY=MO,TU;INTERVAL=3;UNTIL=20320101 +DTSTART:20241231 +INSTANCES:20241231,20280103,20280104,20301230,20301231 + diff --git a/Ical.Net/Evaluation/Evaluator.cs b/Ical.Net/Evaluation/Evaluator.cs index 073251c3..880a2ad3 100644 --- a/Ical.Net/Evaluation/Evaluator.cs +++ b/Ical.Net/Evaluation/Evaluator.cs @@ -55,11 +55,8 @@ protected IDateTime ConvertToIDateTime(DateTime dt, IDateTime referenceDate) protected void IncrementDate(ref DateTime dt, RecurrencePattern pattern, int interval) { - // FIXME: use a more specific exception. if (interval == 0) - { - throw new Exception("Cannot evaluate with an interval of zero. Please use an interval other than zero."); - } + return; var old = dt; switch (pattern.Frequency) diff --git a/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs b/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs index 37633246..7c6fa671 100644 --- a/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs +++ b/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs @@ -211,6 +211,14 @@ private HashSet GetDates(IDateTime seed, DateTime periodStart, DateTim var originalDate = DateUtil.GetSimpleDateTimeData(seed); var seedCopy = DateUtil.GetSimpleDateTimeData(seed); + if ((pattern.Frequency == FrequencyType.Yearly) && (pattern.ByWeekNo.Count != 0)) + { + // Dates in the first or last week of the year could belong weeks that belong to + // the prev/next year, in which case we must adjust that year. This is necessary + // to get the invervals right. + IncrementDate(ref seedCopy, pattern, Calendar.GetIso8601YearOfWeek(seedCopy, pattern.FirstDayOfWeek) - seedCopy.Year); + } + // optimize the start time for selecting candidates // (only applicable where a COUNT is not specified) if (pattern.Count == int.MinValue) @@ -398,34 +406,39 @@ private List GetWeekNoVariants(List dates, RecurrencePattern foreach (var weekNo in GetByWeekNoForYearNormalized(pattern, t.Year)) { var date = t; + + // Make sure we start from a reference date that is in a week that belongs to the current year. + // Its not important that the date lies in a certain week, but that the week belongs to the + // current year and that the week day is preserved. + if (date.Month == 1) + date = date.AddDays(7); + else if (date.Month >= 12) + date = date.AddDays(-7); + // Determine our current week number var currWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek); - while (currWeekNo > weekNo) - { - // If currWeekNo > weekNo, then we're likely at the start of a year - // where currWeekNo could be 52 or 53. If we simply step ahead 7 days - // we should be back to week 1, where we can easily make the calculation - // to move to weekNo. - date = date.AddDays(7); - currWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek); - } // Move ahead to the correct week of the year date = date.AddDays((weekNo - currWeekNo) * 7); - // Step backward single days until we're at the correct DayOfWeek - while (date.DayOfWeek != pattern.FirstDayOfWeek) + // Ignore the week if it doesn't belong to the current year. + if (Calendar.GetIso8601YearOfWeek(date, pattern.FirstDayOfWeek) == t.Year) { - date = date.AddDays(-1); - } + // Step backward single days until we're at the correct DayOfWeek + while (date.DayOfWeek != pattern.FirstDayOfWeek) + { + date = date.AddDays(-1); + } - for (var k = 0; k < 7; k++) - { - weekNoDates.Add(date); - date = date.AddDays(1); + for (var k = 0; k < 7; k++) + { + weekNoDates.Add(date); + date = date.AddDays(1); + } } } } + return weekNoDates; }