INFO: Importing recurrence patterns from Aspose.Recurrence to Aspose.iCalendar

It is possible to import recurrence patterns from Aspose.Recurrence XML format into Aspose.iCalendar patterns.

However, there are some differences in the way the two products generate occurrence dates that sometimes produce different results. See the posts below for more info.

Code Example

///


/// Testing import from XML by Recurrence component (YearlyDayOfWeek)
///

[Test]public void TestFromRecurrenceYearlyDayOfWeek()
{
XmlSerializer ser = new XmlSerializer(typeof(YearlyDayOfWeek));
StringBuilder sb = new StringBuilder();
StringWriter stream = new StringWriter(sb);

//Create Aspose.Recurrence pattern, first friday of April, every 3 years.
YearlyDayOfWeek recur = new YearlyDayOfWeek(3, 4, NthOccurrence.First, DayOfWeek.Friday);
DateTime startDate = new DateTime(2004, 6, 17, 0, 0, 0);
DateTime endDate = new DateTime(2003, 12, 31, 0, 0, 0);
recur.StartDate = startDate;
recur.EndDate = endDate;
recur.EndType = Recurrence.EndType.EndDate;

//Serialize Aspose.Recurrence pattern into XML
ser.Serialize(stream, recur);

//Load Aspose.iCalendar pattern from XML
RecurrencePattern pattern = RecurrencePattern.FromRecurrence(sb.ToString());

//Check Aspose.iCalendar pattern matches the old pattern.
Assert.AreEqual(
“DTSTART;TZID=US-Eastern:20040617T000000\n” +
“RRULE:FREQ=YEARLY;UNTIL=20031231T000000Z;INTERVAL=3;BYMONTH=4;BYDAY=1FR”,
pattern.ToiCalendar());
}

Aspose.Recurrence never includes StartDate into the result set explicitly. StartDate only appears in the result set if it falls onto one of the occurrence dates generated from the pattern.

Aspose.iCalendar always includes StartDate into the result set explicitly, unless it is excluded in EXDATE or EXRULE rule part. This is according to iCalendar specification.

///


/// Shows the difference between Aspose.iCalendar and Aspose.Recurrence due to StartDate.
///

public void TestFullWeekNoFirstDay()
{
RecurrencePattern pattern = RecurrencePattern.FromiCalendar(
“DTSTART:20030114\n” +
“RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,SA”);

DateCollection dates = pattern.GenerateOccurrences(
new DateTime(2003, 1, 14),
new DateTime(2003, 1, 31));

Assert.AreEqual(3, dates.Count);

//This is only included in Aspose.iCalendar, because DTSTART is Tuesday.
//There will be no this occurrence in Aspose.Recurrence.
Assert.AreEqual(dates[0], new DateTime(2003, 1, 14));

Assert.AreEqual(dates[1], new DateTime(2003, 1, 18));
Assert.AreEqual(dates[2], new DateTime(2003, 1, 27));
}

Aspose.Recurrence YearlyEasterRelated pattern is not supported in Aspose.iCalendar and will throw an exception when importing it.

If you create a monthly pattern to repeat on 31st day in Aspose.Recurrence, it will repeat on the last day of every months. That is on 30th in some months and on 28th/29th in February.

Aspose.iCalendar works according to the iCalendar specification and will skip dates that are out of bound for the specified period. For example, if you specify 31st day of every month, you will only get the dates for the months that have 31 days.

If you need to create a pattern in Aspose.iCalendar that repeats on the last day of every month, use:

FREQ=MONTHLY;BYMONTHDAY=-1

Here is a code example that shows the difference between Aspose.Recurrence and Aspose.iCalendar:

///


/// iCalendar works differently from old Aspose.Recurrence for this pattern.
/// iCalendar skips months that don’t have 31st day
///

[Test]
public void TestMonthly31st()
{
RecurrencePattern pattern = RecurrencePattern.FromiCalendar(
“DTSTART:20000707\nRRULE:FREQ=MONTHLY;BYMONTHDAY=31”);
DateCollection dates = pattern.GenerateOccurrences(
new DateTime(2003, 1, 1),
new DateTime(2003, 5, 1));

// Aspose.Recurrence will have more dates here, it will last day for every month.
Assert.AreEqual(2, dates.Count);
Assert.AreEqual(new DateTime(2003, 1, 31), dates[0]);
Assert.AreEqual(new DateTime(2003, 3, 31), dates[1]);
}

You use EXDATE in Aspose.iCalendar to exclude specific dates from the result set. To achieve the same you use SkipExemption in Aspose.Recurrence.

You use COUNT in Aspose.iCalendar to limit the number of occurrences in the result set. To achieve the same, you use MaxOccurrences in Aspose.Recurrence.

When both, EXDATE and COUNT are used in Aspose.iCalendar, the result set could be different from when Aspose.Recurrence SkipExemption and MaxOccurrences are used. Aspose.iCalendar might return fewer occurrences in the result set.

In Aspose.Recurrence, there could be maximum one recurrence rule per recurrence pattern, and in fact, the distinction between recurrence rule and recurrence pattern is never made. Therefore, SkipExemption and MaxOccurrences are both part of the recurrence pattern and even if some excluded date are present, the pattern will always return MaxOccurrences occurrences.

In Aspose.iCalendar, COUNT belong to a recurrence rule (and recurrence rule is part of the recurrence pattern), while EXDATE is like a rule in itself and is part of the recurrence pattern. There could be multiple recurrence rules in one pattern, each can even have its different COUNT value. All recurrence rules are evaluated first, each has its own limit (or no limit) for the number of occurrences. EXDATE is applied at the end. The net result is that Aspose.iCalendar could return fewer occurrences than Aspose.Recurrence.

[Test]
public void TestOffByOne()
{
RecurrencePattern pattern = RecurrencePattern.FromiCalendar(
"DTSTART:20000115\n" +
"RRULE:FREQ=MONTHLY;COUNT=5;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1\n" +
"EXDATE:20000115");

DateCollection dates = pattern.GenerateOccurrences(
new DateTime(2000, 1, 1),
new DateTime(2000, 12, 31));

//Note, that although COUNT is 5, there are only 4 dates in the output.
//This is due to EXDATE removing the dates from the resulting set.
//This is by design, it is even possible to have several RRULE with different COUNT in one pattern.

Assert.AreEqual(4, dates.Count);
Assert.AreEqual(new DateTime(2000, 2, 1), dates[0]);
Assert.AreEqual(new DateTime(2000, 3, 1), dates[1]);
Assert.AreEqual(new DateTime(2000, 4, 3), dates[2]);
Assert.AreEqual(new DateTime(2000, 5, 1), dates[3]);
}




According to iCalendar specification, if there is not information specified in the recurrence rule, the engine takes the required information from StartDate (DTSTART). In some cases it will make Aspose.Recurrence and Aspose.iCalendar different for the “same” pattern.

///


/// In Aspose.Recurrence this used to return no dates, but in iCalendar,
/// the day of week information (not explicitly specified in the pattern)
/// is deducted from the start date. In this case, 20030101 Wed.
///

public void TestNoDaysOn()
{
RecurrencePattern pattern = RecurrencePattern.FromiCalendar(
“DTSTART:20030101\n” +
“RRULE:FREQ=WEEKLY;INTERVAL=1”);

DateCollection dates = pattern.GenerateOccurrences(
new DateTime(2003, 2, 1),
new DateTime(2003, 3, 1));

//All these a Wednesdays becayse DTSTART is Wednesday.
Assert.AreEqual(4, dates.Count);
Assert.AreEqual(dates[0], new DateTime(2003, 2, 5));
Assert.AreEqual(dates[1], new DateTime(2003, 2, 12));
Assert.AreEqual(dates[2], new DateTime(2003, 2, 19));
Assert.AreEqual(dates[3], new DateTime(2003, 2, 26));
}

Hello, romank.

I am having a problem which could be solved easily if I understood how you get the 2 behaviours you explained in your post.

I set up an event with 5 occurrences which should happen on the first 5 Mondays, starting from today. But since today is Wednesday, the occurrences i get are today (Wednesday) + the first 4 Mondays in the future. This is the Aspose.iCalendar you described there. This annoys me and I might have to write a lot of prone-to-error code to correct.

But you also talked about Aspose.Recurrence way of computing occurences, which ignores the start date mentioned if it does not match the recurrence pattern. If today (the start date) is not a Monday, it will be ignored and the occurences will be the first 5 Mondays in the future.

You said: "Aspose.Recurrence never includes StartDate into the result set explicitly. StartDate only appears in the result set if it falls onto one of the occurrence dates generated from the pattern." This is what i want, how exactly do I make things work this way?

Thanks in advance to anyone answering.

Hi,


Thank you for inquiry.

This behavior is due to the iCalendar specifications. The start date of the recurrence pattern always counts as first occurrence, unless it is excluded in ExDates.

You may slightly modify your code as follows:

// new recurrence pattern
RecurrencePattern pattern = new RecurrencePattern();
// set start date as today
pattern.StartDate = DateTime.Today;
// new rule, weekly on every Monday, max 5 occurrences
RecurrenceRule rule = new RecurrenceRule();
rule.Frequency = Frequency.Weekly;
rule.ByDay.Add(DayOfWeek.Monday);
rule.Count = 5;
// add this rule
pattern.RRules.Add(rule);

// start date will always be included
// exclude the start date, if it is not Monday
if (DateTime.Today.DayOfWeek != DayOfWeek.Monday)
{
// exclude today’s date
pattern.ExDates.Add(DateTime.Today);
// set count to 6
rule.Count = 6;
}

// generate the recurrence dates
DateCollection dates = pattern.GenerateOccurrences();
foreach (DateTime date in dates)
{
Console.WriteLine(date.ToString());
}
Console.WriteLine(pattern.ToiCalendar());

Hi, there.


So there is no other way but to manually watch after the first occurence if it matches the pattern? I've thought about it too, before posting on the forum, and I have come to the conclusion that there are lots of scenarios to cover, so the Aspose.Recurrence was a way better alternative.
What exactly is Aspose.Recurrence versus Aspose.iCalendar? Is Aspose.Reccurrence a different implementation of the reccurence? Is it an alternative to Aspose.iCalendar? Is it an older version of the component, no longer available?

Forgive me for being pushy, but can I have access to the Aspose.Recurrence which "never includes StartDate into the result set explicitly. StartDate only appears in the result set if it falls onto one of the occurrence dates generated from the pattern."? This would really save my life, so I will fall back to your suggestion only unless i cannot use the Aspose.Recurrence version.

Hi,


Aspose.Recurrence is very old product which was evolved into Aspose.iCalendar.
Then Aspose.iCalendar was also merged into Aspose.Email.

I am sorry, we cannot provide you the Aspose.Recurrence product because we no longer maintain it’s code. It will also give licensing issues, as we have updated our licensing methodologies too.

That's too bad. Ok then, i will use Aspose.iCalendar with a workaround.

Thanks for replying and have a good day.