Skip to content

Commit

Permalink
Merge pull request #518 from rianjs/ByWeekNo
Browse files Browse the repository at this point in the history
BYWEEKNO & BYMONTH fix & ISO 8601 week number fix
  • Loading branch information
rianjs committed Apr 10, 2021
2 parents 954b675 + 66d443f commit 4112ef9
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 8 deletions.
54 changes: 54 additions & 0 deletions net-core/Ical.Net.CoreUnitTests/RecurrenceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2571,6 +2571,60 @@ public void Bug3007244()
eventIndex: 0
);
}

/// <summary>
/// Tests bug BYWEEKNO not working
/// </summary>
[Test, Category("Recurrence")]
public void BugByWeekNoNotWorking()
{
var start = new DateTime(2019, 1, 1);
var end = new DateTime(2019, 12, 31);
var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=WEEKLY;BYDAY=MO;BYWEEKNO=2"));

var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false);

Assert.AreEqual(1, recurringPeriods.Count);
Assert.AreEqual(new CalDateTime(2019, 1, 7), recurringPeriods.First().StartTime);
}

/// <summary>
/// Tests bug BYMONTH while FREQ=WEEKLY not working
/// </summary>
[Test, Category("Recurrence")]
public void BugByMonthWhileFreqIsWeekly()
{
var start = new DateTime(2020, 1, 1);
var end = new DateTime(2020, 12, 31);
var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=WEEKLY;BYDAY=MO;BYMONTH=1"));

var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false).OrderBy(x => x).ToList();

Assert.AreEqual(4, recurringPeriods.Count);
Assert.AreEqual(new CalDateTime(2020, 1, 6), recurringPeriods[0].StartTime);
Assert.AreEqual(new CalDateTime(2020, 1, 13), recurringPeriods[1].StartTime);
Assert.AreEqual(new CalDateTime(2020, 1, 20), recurringPeriods[2].StartTime);
Assert.AreEqual(new CalDateTime(2020, 1, 27), recurringPeriods[3].StartTime);
}

/// <summary>
/// Tests bug BYMONTH while FREQ=MONTHLY not working
/// </summary>
[Test, Category("Recurrence")]
public void BugByMonthWhileFreqIsMonthly()
{
var start = new DateTime(2020, 1, 1);
var end = new DateTime(2020, 12, 31);
var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=MONTHLY;BYDAY=MO;BYMONTH=1"));

var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false).OrderBy(x => x).ToList();

Assert.AreEqual(4, recurringPeriods.Count);
Assert.AreEqual(new CalDateTime(2020, 1, 6), recurringPeriods[0].StartTime);
Assert.AreEqual(new CalDateTime(2020, 1, 13), recurringPeriods[1].StartTime);
Assert.AreEqual(new CalDateTime(2020, 1, 20), recurringPeriods[2].StartTime);
Assert.AreEqual(new CalDateTime(2020, 1, 27), recurringPeriods[3].StartTime);
}

/// <summary>
/// Tests bug #3119920 - missing weekly occurences
Expand Down
26 changes: 26 additions & 0 deletions net-core/Ical.Net/CalendarExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Globalization;

namespace Ical.Net
{
public static class CalendarExtensions
{
/// <summary>
/// https://blogs.msdn.microsoft.com/shawnste/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net/
/// </summary>
public static int GetIso8601WeekOfYear(this System.Globalization.Calendar calendar, DateTime time, CalendarWeekRule rule, 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);
}

// Return the week of our adjusted day
return calendar.GetWeekOfYear(time, rule, firstDayOfWeek);
}
}
}
27 changes: 19 additions & 8 deletions net-core/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,15 +410,15 @@ private List<DateTime> GetWeekNoVariants(List<DateTime> dates, RecurrencePattern
{
var date = t;
// Determine our current week number
var currWeekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
var currWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, 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.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
currWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
}

// Move ahead to the correct week of the year
Expand Down Expand Up @@ -630,25 +630,30 @@ private List<DateTime> GetAbsWeekDays(DateTime date, WeekDay weekDay, Recurrence
}
else if (pattern.Frequency == FrequencyType.Weekly || pattern.ByWeekNo.Count > 0)
{
var weekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
var weekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);

// construct a list of possible week days..
while (date.DayOfWeek != dayOfWeek)
{
date = date.AddDays(1);
}

var nextWeekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
var currentWeekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
var nextWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, 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.
//So we need an EXRULE for this situation, but only for weekly events
while (currentWeekNo == weekNo || (nextWeekNo < weekNo && currentWeekNo == nextWeekNo && pattern.Frequency == FrequencyType.Weekly))
{
days.Add(date);
if ((pattern.ByWeekNo.Count == 0 || pattern.ByWeekNo.Contains(currentWeekNo))
&& (pattern.ByMonth.Count == 0 || pattern.ByMonth.Contains(date.Month)))
{
days.Add(date);
}

date = date.AddDays(7);
currentWeekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);
currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek,pattern.FirstDayOfWeek);
}
}
else if (pattern.Frequency == FrequencyType.Monthly || pattern.ByMonth.Count > 0)
Expand All @@ -664,7 +669,13 @@ private List<DateTime> GetAbsWeekDays(DateTime date, WeekDay weekDay, Recurrence

while (date.Month == month)
{
days.Add(date);
var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek);

if ((pattern.ByWeekNo.Count == 0 || pattern.ByWeekNo.Contains(currentWeekNo))
&& (pattern.ByMonth.Count == 0 || pattern.ByMonth.Contains(date.Month)))
{
days.Add(date);
}
date = date.AddDays(7);
}
}
Expand Down

0 comments on commit 4112ef9

Please sign in to comment.