@@ -99,8 +99,8 @@ public static bool MatchRecurrence(DateTimeOffset time, TimeWindowFilterSettings
9999 /// Try to find the closest previous recurrence occurrence before the provided time stamp according to the recurrence pattern.
100100 /// <param name="time">A time stamp.</param>
101101 /// <param name="settings">The settings of time window filter.</param>
102- /// <param name="previousOccurrence">The closest orevious occurrence.</param>
103- /// /// <returns>True if the closest previous occurrence is within the recurrence range, false otherwise.</returns>
102+ /// <param name="previousOccurrence">The closest previous occurrence.</param>
103+ /// <returns>True if the closest previous occurrence is within the recurrence range, false otherwise.</returns>
104104 /// </summary>
105105 private static bool TryGetPreviousOccurrence ( DateTimeOffset time , TimeWindowFilterSettings settings , out DateTimeOffset previousOccurrence )
106106 {
@@ -115,10 +115,6 @@ private static bool TryGetPreviousOccurrence(DateTimeOffset time, TimeWindowFilt
115115
116116 string patternType = settings . Recurrence . Pattern . Type ;
117117
118- RecurrenceRange range = settings . Recurrence . Range ;
119-
120- TimeSpan timeZoneOffset = GetRecurrenceTimeZone ( settings ) ;
121-
122118 int numberOfOccurrences ;
123119
124120 if ( string . Equals ( patternType , Daily , StringComparison . OrdinalIgnoreCase ) )
@@ -150,6 +146,10 @@ private static bool TryGetPreviousOccurrence(DateTimeOffset time, TimeWindowFilt
150146 throw new ArgumentException ( nameof ( settings ) ) ;
151147 }
152148
149+ RecurrenceRange range = settings . Recurrence . Range ;
150+
151+ TimeSpan timeZoneOffset = GetRecurrenceTimeZone ( settings ) ;
152+
153153 if ( string . Equals ( range . Type , EndDate , StringComparison . OrdinalIgnoreCase ) )
154154 {
155155 DateTime alignedPreviousOccurrence = previousOccurrence . DateTime + timeZoneOffset - previousOccurrence . Offset ;
@@ -171,6 +171,13 @@ private static bool TryGetPreviousOccurrence(DateTimeOffset time, TimeWindowFilt
171171 return true ;
172172 }
173173
174+ /// <summary>
175+ /// Find the closest previous recurrence occurrence before the provided time stamp according to the "Daily" recurrence pattern.
176+ /// <param name="time">A time stamp.</param>
177+ /// <param name="settings">The settings of time window filter.</param>
178+ /// <param name="previousOccurrence">The closest previous occurrence.</param>
179+ /// <param name="numberOfOccurrences">The number of complete recurrence intervals which have occurred between the time and the recurrence start.</param>
180+ /// </summary>
174181 private static void FindDailyPreviousOccurrence ( DateTimeOffset time , TimeWindowFilterSettings settings , out DateTimeOffset previousOccurrence , out int numberOfOccurrences )
175182 {
176183 RecurrencePattern pattern = settings . Recurrence . Pattern ;
@@ -190,6 +197,13 @@ private static void FindDailyPreviousOccurrence(DateTimeOffset time, TimeWindowF
190197 numberOfOccurrences = numberOfInterval ;
191198 }
192199
200+ /// <summary>
201+ /// Find the closest previous recurrence occurrence before the provided time stamp according to the "Weekly" recurrence pattern.
202+ /// <param name="time">A time stamp.</param>
203+ /// <param name="settings">The settings of time window filter.</param>
204+ /// <param name="previousOccurrence">The closest previous occurrence.</param>
205+ /// <param name="numberOfOccurrences">The number of recurring days of week which have occurred between the time and the recurrence start.</param>
206+ /// </summary>
193207 private static void FindWeeklyPreviousOccurrence ( DateTimeOffset time , TimeWindowFilterSettings settings , out DateTimeOffset previousOccurrence , out int numberOfOccurrences )
194208 {
195209 previousOccurrence = DateTimeOffset . MaxValue ;
@@ -269,6 +283,13 @@ private static void FindWeeklyPreviousOccurrence(DateTimeOffset time, TimeWindow
269283 }
270284 }
271285
286+ /// <summary>
287+ /// Find the closest previous recurrence occurrence before the provided time stamp according to the "AbsoluteMonthly" recurrence pattern.
288+ /// <param name="time">A time stamp.</param>
289+ /// <param name="settings">The settings of time window filter.</param>
290+ /// <param name="previousOccurrence">The closest previous occurrence.</param>
291+ /// <param name="numberOfOccurrences">The number of complete recurrence intervals which have occurred between the time and the recurrence start.</param>
292+ /// </summary>
272293 private static void FindAbsoluteMonthlyPreviousOccurrence ( DateTimeOffset time , TimeWindowFilterSettings settings , out DateTimeOffset previousOccurrence , out int numberOfOccurrences )
273294 {
274295 RecurrencePattern pattern = settings . Recurrence . Pattern ;
@@ -297,6 +318,13 @@ private static void FindAbsoluteMonthlyPreviousOccurrence(DateTimeOffset time, T
297318 numberOfOccurrences = numberOfInterval ;
298319 }
299320
321+ /// <summary>
322+ /// Find the closest previous recurrence occurrence before the provided time stamp according to the "RelativeMonthly" recurrence pattern.
323+ /// <param name="time">A time stamp.</param>
324+ /// <param name="settings">The settings of time window filter.</param>
325+ /// <param name="previousOccurrence">The closest previous occurrence.</param>
326+ /// <param name="numberOfOccurrences">The number of complete recurrence intervals which have occurred between the time and the recurrence start.</param>
327+ /// </summary>
300328 private static void FindRelativeMonthlyPreviousOccurrence ( DateTimeOffset time , TimeWindowFilterSettings settings , out DateTimeOffset previousOccurrence , out int numberOfOccurrences )
301329 {
302330 RecurrencePattern pattern = settings . Recurrence . Pattern ;
@@ -343,6 +371,13 @@ private static void FindRelativeMonthlyPreviousOccurrence(DateTimeOffset time, T
343371 numberOfOccurrences = numberOfInterval ;
344372 }
345373
374+ /// <summary>
375+ /// Find the closest previous recurrence occurrence before the provided time stamp according to the "AbsoluteYearly" recurrence pattern.
376+ /// <param name="time">A time stamp.</param>
377+ /// <param name="settings">The settings of time window filter.</param>
378+ /// <param name="previousOccurrence">The closest previous occurrence.</param>
379+ /// <param name="numberOfOccurrences">The number of complete recurrence intervals which have occurred between the time and the recurrence start.</param>
380+ /// </summary>
346381 private static void FindAbsoluteYearlyPreviousOccurrence ( DateTimeOffset time , TimeWindowFilterSettings settings , out DateTimeOffset previousOccurrence , out int numberOfOccurrences )
347382 {
348383 RecurrencePattern pattern = settings . Recurrence . Pattern ;
@@ -371,6 +406,13 @@ private static void FindAbsoluteYearlyPreviousOccurrence(DateTimeOffset time, Ti
371406 numberOfOccurrences = numberOfInterval ;
372407 }
373408
409+ /// <summary>
410+ /// Find the closest previous recurrence occurrence before the provided time stamp according to the "RelativeYearly" recurrence pattern.
411+ /// <param name="time">A time stamp.</param>
412+ /// <param name="settings">The settings of time window filter.</param>
413+ /// <param name="previousOccurrence">The closest previous occurrence.</param>
414+ /// <param name="numberOfOccurrences">The number of complete recurrence intervals which have occurred between the time and the recurrence start.</param>
415+ /// </summary>
374416 private static void FindRelativeYearlyPreviousOccurrence ( DateTimeOffset time , TimeWindowFilterSettings settings , out DateTimeOffset previousOccurrence , out int numberOfOccurrences )
375417 {
376418 RecurrencePattern pattern = settings . Recurrence . Pattern ;
@@ -389,15 +431,18 @@ private static void FindRelativeYearlyPreviousOccurrence(DateTimeOffset time, Ti
389431
390432 if ( alignedTime . Month < alignedStart . Month )
391433 {
434+ //
435+ // E.g. start: 2023.9 and time: 2024.8
436+ // Not a complete yearly interval
392437 yearGap -= 1 ;
393438 }
394- else if ( alignedTime . Month == alignedStart . Month )
439+ else if ( alignedTime . Month == alignedStart . Month && ! pattern . DaysOfWeek . Any ( day =>
440+ alignedTime >= NthDayOfWeekInTheMonth ( alignedTime , pattern . Index , day ) + alignedStart . TimeOfDay ) )
395441 {
396- if ( ! pattern . DaysOfWeek . Any ( day =>
397- NthDayOfWeekInTheMonth ( alignedTime , pattern . Index , day ) + alignedStart . TimeOfDay <= alignedTime ) )
398- {
399- yearGap -= 1 ;
400- }
442+ //
443+ // E.g. start: 2023.9.1 (the first Friday in 2023.9) and time: 2024.9.2 (the first Friday in 2023.9 is 2024.9.6)
444+ // Not a complete yearly interval
445+ yearGap -= 1 ;
401446 }
402447
403448 int numberOfInterval = yearGap / interval ;
@@ -1278,7 +1323,7 @@ private static bool IsDurationCompliantWithDaysOfWeek(TimeSpan duration, int int
12781323 if ( interval == 1 )
12791324 {
12801325 //
1281- // Check the adjacent week
1326+ // It may across weeks. Check the adjacent week
12821327 date = date . AddDays ( 1 ) ;
12831328
12841329 TimeSpan gap = date - prevOccurrence ;
@@ -1302,10 +1347,17 @@ private static int RemainingDaysOfWeek(int day, int firstDayOfWeek)
13021347 }
13031348 else
13041349 {
1305- return 7 - remainingDays ;
1350+ return WeekDayNumber - remainingDays ;
13061351 }
13071352 }
13081353
1354+ /// <summary>
1355+ /// Find the nth day of week in the month of the date time.
1356+ /// </summary>
1357+ /// <param name="dateTime">A date time.</param>
1358+ /// <param name="index">The index of the day of week in the month.</param>
1359+ /// <param name="dayOfWeek">The day of week.</param>
1360+ /// <returns>The data time of the nth day of week in the month.</returns>
13091361 private static DateTime NthDayOfWeekInTheMonth ( DateTime dateTime , string index , string dayOfWeek )
13101362 {
13111363 var date = new DateTime ( dateTime . Year , dateTime . Month , 1 ) ;
@@ -1317,13 +1369,15 @@ private static DateTime NthDayOfWeekInTheMonth(DateTime dateTime, string index,
13171369 date = date . AddDays ( 1 ) ;
13181370 }
13191371
1320- if ( date . AddDays ( 7 * ( IndexNumber ( index ) - 1 ) ) . Month == dateTime . Month )
1372+ if ( date . AddDays ( WeekDayNumber * ( IndexNumber ( index ) - 1 ) ) . Month == dateTime . Month )
13211373 {
1322- date = date . AddDays ( 7 * ( IndexNumber ( index ) - 1 ) ) ;
1374+ date = date . AddDays ( WeekDayNumber * ( IndexNumber ( index ) - 1 ) ) ;
13231375 }
13241376 else // There is no the 5th day of week in the month
13251377 {
1326- date = date . AddDays ( 21 ) ;
1378+ //
1379+ // Add 3 weeks to reach the fourth day of week in the month
1380+ date = date . AddDays ( WeekDayNumber * 3 ) ;
13271381 }
13281382
13291383 return date ;
0 commit comments