ical-org / ical.net

ical.NET - an open source iCal library for .NET
MIT License
784 stars 231 forks source link

GetOccurrences skips a date with GetOccurences #175

Open AVee opened 7 years ago

AVee commented 7 years ago

After upgrading from DDay.iCal one of our tests failed. The test runs af few cases with weekly repetitions, but just one of them fails. It skips one occurrence which DDay.iCal did generate. This happens to be the first occurrence of a new year, which may or may not be relevant.

I've compressed the test into a single console app which demonstrates the different behaviour:

static void Main(string[] args)
{
    string rrule = "FREQ=WEEKLY;BYDAY=FR;INTERVAL=1;UNTIL=20130430T235959";
    DateTime startdate = new DateTime(2012, 10, 12, 7, 00, 00);
    DateTime until = new DateTime(2013, 4, 30, 17, 30, 00);
    TimeSpan duration = new TimeSpan(10, 30, 0);

    // Old DDay.iCal
    DDay.iCal.RecurrencePattern rr = new DDay.iCal.RecurrencePattern(rrule);
    rr.RestrictionType = DDay.iCal.RecurrenceRestrictionType.RestrictHourly;

    DDay.iCal.IEvent evt = new DDay.iCal.Event();
    evt.RecurrenceRules.Add(rr);
    evt.Start = new DDay.iCal.iCalDateTime(startdate);
    evt.Duration = duration;

    // Both statements return the same result
    //IList<DDay.iCal.Occurrence> occurences = evt.GetOccurrences(startdate, until);
    IList<DDay.iCal.Occurrence> occurences = DDay.iCal.RecurrenceUtil.GetOccurrences(evt, new DDay.iCal.iCalDateTime(startdate.AddHours(-1)), new DDay.iCal.iCalDateTime(until), false);

    Debug.Assert(occurences.Count == 29);
    occurences.All(x => { Debug.WriteLine(x.Period.StartTime.Value); return true; });
    DateTime start = new DateTime(2012, 10, 12, 7, 00, 00);
    foreach (DDay.iCal.Occurrence rec in occurences)
    {
        Debug.Assert(DayOfWeek.Friday == rec.Period.StartTime.DayOfWeek);
        Debug.Assert(start == rec.Period.StartTime.Value);
        Debug.Assert(start.AddMinutes(630) == rec.Period.EndTime.Value);
        start = start.AddDays(7);
    }
    Debug.WriteLine("-------------");

    // iCal.net
    Ical.Net.DataTypes.RecurrencePattern rr2 = new Ical.Net.DataTypes.RecurrencePattern(rrule);
    rr2.RestrictionType = Ical.Net.RecurrenceRestrictionType.RestrictHourly;

    Ical.Net.Interfaces.Components.IEvent evt2 = new Ical.Net.Event();
    evt2.RecurrenceRules.Add(rr2);
    evt2.Start = new Ical.Net.DataTypes.CalDateTime(startdate);
    evt2.Duration = duration;

    // Both statements below return the same results, but they skip Jan 4 2013. Happens in 2.2.14 and in 2.2.15
    //HashSet<Ical.Net.DataTypes.Occurrence> occurences2 = evt2.GetOccurrences(startdate, until);
    HashSet<Ical.Net.DataTypes.Occurrence> occurences2 = Ical.Net.Utility.RecurrenceUtil.GetOccurrences(evt2, new Ical.Net.DataTypes.CalDateTime(startdate.AddHours(-1)), new Ical.Net.DataTypes.CalDateTime(until), false);

    occurences2.All(x => { Debug.WriteLine(x.Period.StartTime.Value); return true; });

    Debug.Assert(occurences2.Count == 29);
    start = new DateTime(2012, 10, 12, 7, 00, 00);
    foreach (Ical.Net.DataTypes.Occurrence rec in occurences2.OrderBy(x => x.Period.StartTime.Value))
    {
        Debug.Assert(DayOfWeek.Friday == rec.Period.StartTime.DayOfWeek);
        Debug.Assert(start == rec.Period.StartTime.Value);
        Debug.Assert(start.AddMinutes(630) == rec.Period.EndTime.Value);
        start = start.AddDays(7);
    }
}
AVee commented 7 years ago

Complete VS2013 project: ConsoleApplication3.zip