diff --git a/Ical.Net.Tests/contrib/libical/icalrecur_test.out b/Ical.Net.Tests/contrib/libical/icalrecur_test.out
index 7b1dde8cd..87e3cbc73 100644
--- a/Ical.Net.Tests/contrib/libical/icalrecur_test.out
+++ b/Ical.Net.Tests/contrib/libical/icalrecur_test.out
@@ -357,11 +357,10 @@ START-AT:20210302T100000
INSTANCES:20210302T102000,20210302T112000,20210302T122000,20210302T132000,20210302T142000,20210302T152000,20210302T162000,20210302T172000,20210302T182000,20210302T192000,20210302T202000,20210302T212000,20210302T222000,20210302T232000
PREV-INSTANCES:20210302T092000,20210302T082000,20210302T072000,20210302T062000,20210302T052000,20210302T042000,20210302T032000,20210302T022000,20210302T012000,20210302T002000,20210301T232000,20210301T222000,20210301T212000,20210301T202000,20210301T192000,20210301T182000,20210301T172000,20210301T162000,20210301T152000,20210301T142000
-# TODO: FIX (see https://github.com/ical-org/ical.net/issues/618)
-# RRULE:FREQ=YEARLY;BYWEEKNO=6;BYDAY=TU;WKST=TH;UNTIL=20210612T000000Z
-# DTSTART:20180206T080001
-# INSTANCES:20180213T080001,20190212T080001,20200211T080001,20210209T080001
-# PREV-INSTANCES:20210209T080001,20200211T080001,20190212T080001,20180213T080001
+RRULE:FREQ=YEARLY;BYWEEKNO=6;BYDAY=TU;WKST=TH;UNTIL=20210612T000000Z
+DTSTART:20180213T080001
+INSTANCES:20180213T080001,20190212T080001,20200211T080001,20210209T080001
+PREV-INSTANCES:20210209T080001,20200211T080001,20190212T080001,20180213T080001
RRULE:FREQ=DAILY;BYMINUTE=1,2,3,4;INTERVAL=2;COUNT=3
DTSTART:20241018
diff --git a/Ical.Net/CalendarExtensions.cs b/Ical.Net/CalendarExtensions.cs
index 0f80956f1..6848ef293 100644
--- a/Ical.Net/CalendarExtensions.cs
+++ b/Ical.Net/CalendarExtensions.cs
@@ -11,20 +11,32 @@ namespace Ical.Net;
public static class CalendarExtensions
{
///
- /// https://blogs.msdn.microsoft.com/shawnste/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net/
+ /// Calculate the week number according to ISO.8601, as required by RFC 5545.
///
- public static int GetIso8601WeekOfYear(this System.Globalization.Calendar calendar, DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
+ public static int GetIso8601WeekOfYear(this System.Globalization.Calendar calendar, DateTime time, DayOfWeek firstDayOfWeek)
{
- // Seriously cheat. If its Monday, Tuesday or Wednesday, then it'll
- // be the same week# as whatever Thursday, Friday or Saturday are,
- // and we always get those right
- var day = calendar.GetDayOfWeek(time);
- if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
- {
- time = time.AddDays(3);
- }
+ // A week is defined as a
+ // seven day period, starting on the day of the week defined to be
+ // the week start(see WKST). Week number one of the calendar year
+ // is the first week that contains at least four (4) days in that
+ // calendar year.
- // Return the week of our adjusted day
- return calendar.GetWeekOfYear(time, rule, firstDayOfWeek);
+ // We add 3 to make sure the test date is in the 'right' year, because
+ // otherwise we might end up with week 53 in a year that only has 52.
+ var tTest = GetStartOfWeek(time, firstDayOfWeek).AddDays(3);
+ var res = calendar.GetWeekOfYear(tTest, CalendarWeekRule.FirstFourDayWeek, firstDayOfWeek);
+
+ return res;
+ }
+
+ ///
+ /// Calculate and return the date that represents the first day of the week the given date is
+ /// in, according to the week numbering required by RFC 5545.
+ ///
+ private static DateTime GetStartOfWeek(this DateTime t, DayOfWeek firstDayOfWeek)
+ {
+ var t0 = ((int) firstDayOfWeek) % 7;
+ var tn = ((int) t.DayOfWeek) % 7;
+ return t.AddDays(-((tn + 7 - t0) % 7));
}
-}
\ No newline at end of file
+}
diff --git a/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs b/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs
index e5edcd30c..118bdbc0d 100644
--- a/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs
+++ b/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs
@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using Ical.Net.DataTypes;
using Ical.Net.Utility;
@@ -400,7 +399,7 @@ private List GetWeekNoVariants(List dates, RecurrencePattern
{
var date = t;
// Determine our current week number
- var currWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
+ var currWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek);
while (currWeekNo > weekNo)
{
// If currWeekNo > weekNo, then we're likely at the start of a year
@@ -408,7 +407,7 @@ private List GetWeekNoVariants(List dates, RecurrencePattern
// 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, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
+ currWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek);
}
// Move ahead to the correct week of the year
@@ -629,7 +628,7 @@ private List GetAbsWeekDays(DateTime date, WeekDay weekDay, Recurrence
}
else if (pattern.Frequency == FrequencyType.Weekly || pattern.ByWeekNo.Count > 0)
{
- var weekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
+ var weekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek);
// Go to the first day of the week
date = date.AddDays(-GetWeekDayOffset(date, pattern.FirstDayOfWeek));
@@ -640,8 +639,8 @@ private List GetAbsWeekDays(DateTime date, WeekDay weekDay, Recurrence
date = date.AddDays(1);
}
- var nextWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
- var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
+ var nextWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek);
+ var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek);
//When we manage weekly recurring pattern and we have boundary case:
//Weekdays: Dec 31, Jan 1, Feb 1, Mar 1, Apr 1, May 1, June 1, Dec 31 - It's the 53th week of the year, but all another are 1st week number.
@@ -655,7 +654,7 @@ private List GetAbsWeekDays(DateTime date, WeekDay weekDay, Recurrence
}
date = date.AddDays(7);
- currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
+ currentWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek);
}
}
else if (pattern.Frequency == FrequencyType.Monthly || pattern.ByMonth.Count > 0)
@@ -671,7 +670,7 @@ private List GetAbsWeekDays(DateTime date, WeekDay weekDay, Recurrence
while (date.Month == month)
{
- var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
+ var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, pattern.FirstDayOfWeek);
if ((pattern.ByWeekNo.Count == 0 || pattern.ByWeekNo.Contains(currentWeekNo))
&& (pattern.ByMonth.Count == 0 || pattern.ByMonth.Contains(date.Month)))