diff --git a/Ical.Net.Tests/CalendarEventTest.cs b/Ical.Net.Tests/CalendarEventTest.cs index 4f599d26..36586bfa 100644 --- a/Ical.Net.Tests/CalendarEventTest.cs +++ b/Ical.Net.Tests/CalendarEventTest.cs @@ -249,29 +249,29 @@ public void RrulesAreSignificantTests() Assert.That(testRrule.GetHashCode(), Is.Not.EqualTo(simpleEvent.GetHashCode())); var testRdate = GetSimpleEvent(); - testRdate.RecurrenceDates = new List { new PeriodList { new Period(new CalDateTime(_now)) } }; + testRdate.RecurrenceDatesPeriodLists = new List { new PeriodList { new Period(new CalDateTime(_now)) } }; Assert.That(testRdate, Is.Not.EqualTo(simpleEvent)); Assert.That(testRdate.GetHashCode(), Is.Not.EqualTo(simpleEvent.GetHashCode())); } private static List GetSimpleRecurrenceList() => new List { new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 } }; - private static List GetExceptionDates() - => new List { new PeriodList { new Period(new CalDateTime(_now.AddDays(1).Date)) } }; + private static List GetExceptionDates() + => new List { new CalDateTime(_now.AddDays(1).Date) }; [Test] public void EventWithRecurrenceAndExceptionComparison() { var vEvent = GetSimpleEvent(); vEvent.RecurrenceRules = GetSimpleRecurrenceList(); - vEvent.ExceptionDates = GetExceptionDates(); + vEvent.ExceptionDates.AddRange(GetExceptionDates()); var calendar = new Calendar(); calendar.Events.Add(vEvent); var vEvent2 = GetSimpleEvent(); vEvent2.RecurrenceRules = GetSimpleRecurrenceList(); - vEvent2.ExceptionDates = GetExceptionDates(); + vEvent2.ExceptionDates.AddRange(GetExceptionDates()); var cal2 = new Calendar(); cal2.Events.Add(vEvent2); @@ -283,8 +283,7 @@ public void EventWithRecurrenceAndExceptionComparison() { Assert.That(eventB.RecurrenceRules.First(), Is.EqualTo(eventA.RecurrenceRules.First())); Assert.That(eventB.RecurrenceRules.First().GetHashCode(), Is.EqualTo(eventA.RecurrenceRules.First().GetHashCode())); - Assert.That(eventB.ExceptionDates.First(), Is.EqualTo(eventA.ExceptionDates.First())); - Assert.That(eventB.ExceptionDates.First().GetHashCode(), Is.EqualTo(eventA.ExceptionDates.First().GetHashCode())); + Assert.That(eventB.ExceptionDates.GetAllDates().First(), Is.EqualTo(eventA.ExceptionDates.GetAllDates().First())); Assert.That(eventB.GetHashCode(), Is.EqualTo(eventA.GetHashCode())); Assert.That(eventB, Is.EqualTo(eventA)); Assert.That(cal2, Is.EqualTo(calendar)); @@ -309,7 +308,7 @@ public void AddingExdateToEventShouldNotBeEqualToOriginal() var deserializedNoExDate = Calendar.Load(serialized); Assert.That(deserializedNoExDate, Is.EqualTo(cal1)); - vEvent.ExceptionDates = GetExceptionDates(); + vEvent.ExceptionDates.AddRange(GetExceptionDates()); serialized = serializer.SerializeToString(cal1); var deserializedWithExDate = Calendar.Load(serialized); diff --git a/Ical.Net.Tests/DeserializationTests.cs b/Ical.Net.Tests/DeserializationTests.cs index 5a596c72..ea995ca6 100644 --- a/Ical.Net.Tests/DeserializationTests.cs +++ b/Ical.Net.Tests/DeserializationTests.cs @@ -267,36 +267,37 @@ public void Encoding3() [Test] public void Event8() { - var sr = @"BEGIN:VCALENDAR -VERSION:2.0 -PRODID:-//Apple Computer\, Inc//iCal 1.0//EN -CALSCALE:GREGORIAN -BEGIN:VEVENT -CREATED:20070404T211714Z -DTEND:20070407T010000Z -DTSTAMP:20070404T211714Z -DTSTART:20070406T230000Z -DURATION:PT2H -RRULE:FREQ=WEEKLY;UNTIL=20070801T070000Z;BYDAY=FR -SUMMARY:Friday Meetings -DTSTAMP:20040103T033800Z -SEQUENCE:1 -UID:fd940618-45e2-4d19-b118-37fd7a8e3906 -END:VEVENT -BEGIN:VEVENT -CREATED:20070404T204310Z -DTEND:20070416T030000Z -DTSTAMP:20070404T204310Z -DTSTART:20070414T200000Z -DURATION:P1DT7H -RRULE:FREQ=DAILY;COUNT=12;BYDAY=SA,SU -SUMMARY:Weekend Yea! -DTSTAMP:20040103T033800Z -SEQUENCE:1 -UID:ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 -END:VEVENT -END:VCALENDAR -"; + var sr = """ + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Apple Computer\, Inc//iCal 1.0//EN + CALSCALE:GREGORIAN + BEGIN:VEVENT + CREATED:20070404T211714Z + DTEND:20070407T010000Z + DTSTAMP:20070404T211714Z + DTSTART:20070406T230000Z + DURATION:PT2H + RRULE:FREQ=WEEKLY;UNTIL=20070801T070000Z;BYDAY=FR + SUMMARY:Friday Meetings + DTSTAMP:20040103T033800Z + SEQUENCE:1 + UID:fd940618-45e2-4d19-b118-37fd7a8e3906 + END:VEVENT + BEGIN:VEVENT + CREATED:20070404T204310Z + DTEND:20070416T030000Z + DTSTAMP:20070404T204310Z + DTSTART:20070414T200000Z + DURATION:P1DT7H + RRULE:FREQ=DAILY;COUNT=12;BYDAY=SA,SU + SUMMARY:Weekend Yea! + DTSTAMP:20040103T033800Z + SEQUENCE:1 + UID:ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 + END:VEVENT + END:VCALENDAR + """; var iCal = Calendar.Load(sr); Assert.That(iCal.Events.Count == 2, Is.True, "There should be 2 events in the parsed calendar"); Assert.That(iCal.Events["fd940618-45e2-4d19-b118-37fd7a8e3906"], Is.Not.Null, "Event fd940618-45e2-4d19-b118-37fd7a8e3906 should exist in the calendar"); @@ -351,25 +352,46 @@ public void Google1() public void RecurrenceDates1() { var iCal = Calendar.Load(IcsFiles.RecurrenceDates1); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Events.First().RecurrenceDates, Has.Count.EqualTo(3)); + + var expectedStartTimes = new List + { + // date are unique + new CalDateTime(1997, 7, 14, 12, 30, 0, CalDateTime.UtcTzId), + new CalDateTime(1996, 4, 3, 2, 0, 0, CalDateTime.UtcTzId), + new CalDateTime(1996, 4, 4, 1, 0, 0, CalDateTime.UtcTzId), + new CalDateTime(1997, 1, 1), + new CalDateTime(1997, 1, 20), + new CalDateTime(1997, 2, 17), + new CalDateTime(1997, 4, 21), + new CalDateTime(1997, 5, 26), + new CalDateTime(1997, 7, 4), + new CalDateTime(1997, 9, 1), + new CalDateTime(1997, 10, 14), + new CalDateTime(1997, 11, 28), + new CalDateTime(1997, 11, 29), + new CalDateTime(1997, 12, 25) + }; + + var expectedEndTime = new CalDateTime(new DateTime(1996, 4, 3, 4, 0, 0, DateTimeKind.Utc)); + + var actualStartTimes = iCal.Events[0].RecurrenceDates.GetAllPeriods() + .Select(p => p.StartTime) + .Union(iCal.Events[0].RecurrenceDates.GetAllDates()) + .ToList(); Assert.Multiple(() => { - Assert.That(iCal.Events.First().RecurrenceDates[0][0].StartTime, Is.EqualTo((CalDateTime)new DateTime(1997, 7, 14, 12, 30, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].StartTime, Is.EqualTo((CalDateTime)new DateTime(1996, 4, 3, 2, 0, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].EndTime, Is.EqualTo((CalDateTime)new DateTime(1996, 4, 3, 4, 0, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[2][0].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][1].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 20))); - Assert.That(iCal.Events.First().RecurrenceDates[2][2].StartTime, Is.EqualTo(new CalDateTime(1997, 2, 17))); - Assert.That(iCal.Events.First().RecurrenceDates[2][3].StartTime, Is.EqualTo(new CalDateTime(1997, 4, 21))); - Assert.That(iCal.Events.First().RecurrenceDates[2][4].StartTime, Is.EqualTo(new CalDateTime(1997, 5, 26))); - Assert.That(iCal.Events.First().RecurrenceDates[2][5].StartTime, Is.EqualTo(new CalDateTime(1997, 7, 4))); - Assert.That(iCal.Events.First().RecurrenceDates[2][6].StartTime, Is.EqualTo(new CalDateTime(1997, 9, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][7].StartTime, Is.EqualTo(new CalDateTime(1997, 10, 14))); - Assert.That(iCal.Events.First().RecurrenceDates[2][8].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 28))); - Assert.That(iCal.Events.First().RecurrenceDates[2][9].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 29))); - Assert.That(iCal.Events.First().RecurrenceDates[2][10].StartTime, Is.EqualTo(new CalDateTime(1997, 12, 25))); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + Assert.That(iCal.Events[0].RecurrenceDatesPeriodLists, Has.Count.EqualTo(3)); + Assert.That(actualStartTimes, Has.Count.EqualTo(expectedStartTimes.Count)); + + foreach (var date in expectedStartTimes) + { + Assert.That(actualStartTimes.Single(dt => dt.Equals(date)), + Is.EqualTo(date), "Should contain " + date); + } + + Assert.That(iCal.Events[0].RecurrenceDates.Contains(new Period(expectedStartTimes[1], expectedEndTime))); }); } @@ -549,13 +571,14 @@ public void Property1() [TestCase(false)] public void KeepApartDtEndAndDuration_Tests(bool useDtEnd) { - var calStr = $@"BEGIN:VCALENDAR -BEGIN:VEVENT -DTSTART:20070406T230000Z -{(useDtEnd ? "DTEND:20070407T010000Z" : "DURATION:PT1H")} -END:VEVENT -END:VCALENDAR -"; + var calStr = $""" + BEGIN:VCALENDAR + BEGIN:VEVENT + DTSTART:20070406T230000Z + {(useDtEnd ? "DTEND:20070407T010000Z" : "DURATION:PT1H")} + END:VEVENT + END:VCALENDAR + """; var calendar = Calendar.Load(calStr); diff --git a/Ical.Net.Tests/PeriodListWrapperTests.cs b/Ical.Net.Tests/PeriodListWrapperTests.cs index 378df5db..5c1771aa 100644 --- a/Ical.Net.Tests/PeriodListWrapperTests.cs +++ b/Ical.Net.Tests/PeriodListWrapperTests.cs @@ -23,7 +23,7 @@ public void AddExDateTime_ShouldCreate_DedicatePeriodList() var cal = new Calendar(); var evt = new CalendarEvent(); cal.Events.Add(evt); - var exDates = new ExceptionDates(evt.ExceptionDates); + var exDates = evt.ExceptionDates; exDates // Add date-only .Add(new CalDateTime(2025, 1, 1)) @@ -45,22 +45,22 @@ public void AddExDateTime_ShouldCreate_DedicatePeriodList() Assert.Multiple(() => { // 2 dedicate PeriodList objects - Assert.That(evt.ExceptionDates, Has.Count.EqualTo(3)); + Assert.That(evt.ExceptionDatesPeriodLists, Has.Count.EqualTo(3)); // First PeriodList is date-only - Assert.That(evt.ExceptionDates[0], Has.Count.EqualTo(2)); - Assert.That(evt.ExceptionDates[0].TzId, Is.Null); - Assert.That(evt.ExceptionDates[0].PeriodKind, Is.EqualTo(PeriodKind.DateOnly)); + Assert.That(evt.ExceptionDatesPeriodLists[0], Has.Count.EqualTo(2)); + Assert.That(evt.ExceptionDatesPeriodLists[0].TzId, Is.Null); + Assert.That(evt.ExceptionDatesPeriodLists[0].PeriodKind, Is.EqualTo(PeriodKind.DateOnly)); // Second PeriodList is date-time UTC - Assert.That(evt.ExceptionDates[1], Has.Count.EqualTo(1)); - Assert.That(evt.ExceptionDates[1].TzId, Is.EqualTo(CalDateTime.UtcTzId)); - Assert.That(evt.ExceptionDates[1].PeriodKind, Is.EqualTo(PeriodKind.DateTime)); + Assert.That(evt.ExceptionDatesPeriodLists[1], Has.Count.EqualTo(1)); + Assert.That(evt.ExceptionDatesPeriodLists[1].TzId, Is.EqualTo(CalDateTime.UtcTzId)); + Assert.That(evt.ExceptionDatesPeriodLists[1].PeriodKind, Is.EqualTo(PeriodKind.DateTime)); // Second PeriodList is date-time - Assert.That(evt.ExceptionDates[2], Has.Count.EqualTo(2)); - Assert.That(evt.ExceptionDates[2].TzId, Is.EqualTo("Europe/Berlin")); - Assert.That(evt.ExceptionDates[2].PeriodKind, Is.EqualTo(PeriodKind.DateTime)); + Assert.That(evt.ExceptionDatesPeriodLists[2], Has.Count.EqualTo(2)); + Assert.That(evt.ExceptionDatesPeriodLists[2].TzId, Is.EqualTo("Europe/Berlin")); + Assert.That(evt.ExceptionDatesPeriodLists[2].PeriodKind, Is.EqualTo(PeriodKind.DateTime)); Assert.That(serialized, Does.Contain( @@ -77,7 +77,7 @@ public void AddExDateTime_ShouldCreate_DedicatePeriodList() public void RemoveExDateTime_ShouldRemove_FromPeriodList() { var evt = new CalendarEvent(); - var exDates = new ExceptionDates(evt.ExceptionDates); + var exDates = evt.ExceptionDates; var dateOnly = new CalDateTime(2025, 1, 1); var dateTime = new CalDateTime(2025, 1, 1, 10, 11, 12, "Europe/Berlin"); @@ -95,10 +95,10 @@ public void RemoveExDateTime_ShouldRemove_FromPeriodList() Assert.That(dateOnlySuccess, Is.True); Assert.That(dateTimeSuccess, Is.True); Assert.That(dateOnlyFail, Is.True); - Assert.That(evt.ExceptionDates[0], Has.Count.EqualTo(1)); - Assert.That(evt.ExceptionDates[1], Is.Empty); + Assert.That(evt.ExceptionDatesPeriodLists[0], Has.Count.EqualTo(1)); + Assert.That(evt.ExceptionDatesPeriodLists[1], Is.Empty); // Empty lists should work as well - evt.ExceptionDates.Clear(); + evt.ExceptionDatesPeriodLists.Clear(); Assert.That(() => exDates.Remove(dateTime), Is.False); }); } @@ -113,7 +113,7 @@ public void AddRDateTime_ShouldCreate_DedicatePeriodList() var cal = new Calendar(); var evt = new CalendarEvent(); cal.Events.Add(evt); - var recDates = new RecurrenceDates(evt.RecurrenceDates); + var recDates = evt.RecurrenceDates; recDates // Add date-only .Add(new CalDateTime(2025, 1, 1)) @@ -131,17 +131,17 @@ public void AddRDateTime_ShouldCreate_DedicatePeriodList() Assert.Multiple(() => { // 2 dedicate PeriodList objects - Assert.That(evt.RecurrenceDates, Has.Count.EqualTo(2)); + Assert.That(evt.RecurrenceDatesPeriodLists, Has.Count.EqualTo(2)); // First PeriodList is date-only - Assert.That(evt.RecurrenceDates[0], Has.Count.EqualTo(2)); - Assert.That(evt.RecurrenceDates[0].TzId, Is.Null); - Assert.That(evt.RecurrenceDates[0].PeriodKind, Is.EqualTo(PeriodKind.DateOnly)); + Assert.That(evt.RecurrenceDatesPeriodLists[0], Has.Count.EqualTo(2)); + Assert.That(evt.RecurrenceDatesPeriodLists[0].TzId, Is.Null); + Assert.That(evt.RecurrenceDatesPeriodLists[0].PeriodKind, Is.EqualTo(PeriodKind.DateOnly)); // Third PeriodList is date-time - Assert.That(evt.RecurrenceDates[1], Has.Count.EqualTo(2)); - Assert.That(evt.RecurrenceDates[1].TzId, Is.EqualTo("Europe/Berlin")); - Assert.That(evt.RecurrenceDates[1].PeriodKind, Is.EqualTo(PeriodKind.DateTime)); + Assert.That(evt.RecurrenceDatesPeriodLists[1], Has.Count.EqualTo(2)); + Assert.That(evt.RecurrenceDatesPeriodLists[1].TzId, Is.EqualTo("Europe/Berlin")); + Assert.That(evt.RecurrenceDatesPeriodLists[1].PeriodKind, Is.EqualTo(PeriodKind.DateTime)); Assert.That(serialized, Does.Contain( @@ -159,7 +159,7 @@ public void AddRPeriod_ShouldCreate_DedicatePeriodList() var cal = new Calendar(); var evt = new CalendarEvent(); cal.Events.Add(evt); - var recPeriod = new RecurrenceDates(evt.RecurrenceDates); + var recPeriod = evt.RecurrenceDates; recPeriod // Add date-only period @@ -194,22 +194,22 @@ public void AddRPeriod_ShouldCreate_DedicatePeriodList() Assert.Multiple(() => { // 2 dedicate PeriodList objects - Assert.That(evt.RecurrenceDates, Has.Count.EqualTo(3)); + Assert.That(evt.RecurrenceDatesPeriodLists, Has.Count.EqualTo(3)); // First PeriodList has date-only periods - Assert.That(evt.RecurrenceDates[0], Has.Count.EqualTo(3)); - Assert.That(evt.RecurrenceDates[0].TzId, Is.Null); - Assert.That(evt.RecurrenceDates[0].PeriodKind, Is.EqualTo(PeriodKind.Period)); + Assert.That(evt.RecurrenceDatesPeriodLists[0], Has.Count.EqualTo(3)); + Assert.That(evt.RecurrenceDatesPeriodLists[0].TzId, Is.Null); + Assert.That(evt.RecurrenceDatesPeriodLists[0].PeriodKind, Is.EqualTo(PeriodKind.Period)); // Second PeriodList has UTC date-time periods - Assert.That(evt.RecurrenceDates[1], Has.Count.EqualTo(2)); - Assert.That(evt.RecurrenceDates[1].TzId, Is.EqualTo("UTC")); - Assert.That(evt.RecurrenceDates[1].PeriodKind, Is.EqualTo(PeriodKind.Period)); + Assert.That(evt.RecurrenceDatesPeriodLists[1], Has.Count.EqualTo(2)); + Assert.That(evt.RecurrenceDatesPeriodLists[1].TzId, Is.EqualTo("UTC")); + Assert.That(evt.RecurrenceDatesPeriodLists[1].PeriodKind, Is.EqualTo(PeriodKind.Period)); // Third PeriodList has zoned date-time with duration - Assert.That(evt.RecurrenceDates[2], Has.Count.EqualTo(1)); - Assert.That(evt.RecurrenceDates[2].TzId, Is.EqualTo("Europe/Vienna")); - Assert.That(evt.RecurrenceDates[2].PeriodKind, Is.EqualTo(PeriodKind.Period)); + Assert.That(evt.RecurrenceDatesPeriodLists[2], Has.Count.EqualTo(1)); + Assert.That(evt.RecurrenceDatesPeriodLists[2].TzId, Is.EqualTo("Europe/Vienna")); + Assert.That(evt.RecurrenceDatesPeriodLists[2].PeriodKind, Is.EqualTo(PeriodKind.Period)); Assert.That(serialized, Does.Contain( @@ -229,7 +229,7 @@ public void AddRPeriod_ShouldCreate_DedicatePeriodList() public void RemoveRDateTime_ShouldRemove_FromPeriodList() { var evt = new CalendarEvent(); - var recDates = new RecurrenceDates(evt.RecurrenceDates); + var recDates = evt.RecurrenceDates; var period1 = new Period(new CalDateTime(2025, 1, 1), Duration.FromDays(5)); var period2 = new Period(new CalDateTime(2025, 1, 1, 10, 0, 0, "Europe/Berlin"), Duration.FromHours(6)); @@ -246,8 +246,8 @@ public void RemoveRDateTime_ShouldRemove_FromPeriodList() Assert.That(period2Success, Is.True); Assert.That(period1Success, Is.True); Assert.That(period2Fail, Is.True); - Assert.That(evt.RecurrenceDates[0], Has.Count.EqualTo(1)); - Assert.That(evt.RecurrenceDates[1], Is.Empty); + Assert.That(evt.RecurrenceDatesPeriodLists[0], Has.Count.EqualTo(1)); + Assert.That(evt.RecurrenceDatesPeriodLists[1], Is.Empty); }); } @@ -255,7 +255,7 @@ public void RemoveRDateTime_ShouldRemove_FromPeriodList() public void Contains_ShouldReturnTrue_IfPeriodExists() { var evt = new CalendarEvent(); - var recDates = new RecurrenceDates(evt.RecurrenceDates); + var recDates = evt.RecurrenceDates; var period1 = new Period(new CalDateTime(2025, 1, 1), Duration.FromDays(5)); var period2 = new Period(new CalDateTime(2025, 1, 1, 10, 0, 0, "Europe/Berlin"), Duration.FromHours(6)); @@ -273,7 +273,7 @@ public void Contains_ShouldReturnTrue_IfPeriodExists() public void Contains_ShouldReturnFalse_IfPeriodDoesNotExist() { var evt = new CalendarEvent(); - var recDates = new RecurrenceDates(evt.RecurrenceDates); + var recDates = evt.RecurrenceDates; var period1 = new Period(new CalDateTime(2025, 1, 1), Duration.FromDays(5)); var period2 = new Period(new CalDateTime(2025, 1, 1, 10, 0, 0, "Europe/Berlin"), Duration.FromHours(6)); @@ -295,7 +295,7 @@ public void Contains_ShouldReturnFalse_IfPeriodDoesNotExist() public void Clear_ShouldRemoveAllPeriods() { var evt = new CalendarEvent(); - var exDates = new ExceptionDates(evt.ExceptionDates); + var exDates = evt.ExceptionDates; exDates .Add(new CalDateTime(2025, 1, 1)) @@ -303,14 +303,14 @@ public void Clear_ShouldRemoveAllPeriods() exDates.Clear(); - Assert.That(evt.ExceptionDates, Is.Empty); + Assert.That(evt.ExceptionDatesPeriodLists, Is.Empty); } [Test] public void Contains_ShouldReturnTrue_IfDateExists() { var evt = new CalendarEvent(); - var exDates = new ExceptionDates(evt.ExceptionDates); + var exDates = evt.ExceptionDates; var dateOnly = new CalDateTime(2025, 1, 1); var dateTime = new CalDateTime(2025, 1, 1, 10, 11, 12, "Europe/Berlin"); @@ -328,7 +328,7 @@ public void Contains_ShouldReturnTrue_IfDateExists() public void Contains_ShouldReturnFalse_IfDateDoesNotExist() { var evt = new CalendarEvent(); - var exDates = new ExceptionDates(evt.ExceptionDates); + var exDates = evt.ExceptionDates; var dateOnly = new CalDateTime(2025, 1, 1); var dateTime = new CalDateTime(2025, 1, 1, 10, 11, 12, "Europe/Berlin"); diff --git a/Ical.Net.Tests/RecurrenceTests.cs b/Ical.Net.Tests/RecurrenceTests.cs index ada24124..b98cbb14 100644 --- a/Ical.Net.Tests/RecurrenceTests.cs +++ b/Ical.Net.Tests/RecurrenceTests.cs @@ -3292,14 +3292,13 @@ public void AddExDateToEventAfterGetOccurrencesShouldRecomputeResult() Assert.That(occurrences, Has.Count.EqualTo(5)); var exDate = _now.AddDays(1); - var periodList = new PeriodList() { new CalDateTime(exDate, false) }; - e.ExceptionDates.Add(periodList); + e.ExceptionDates.Add(new CalDateTime(exDate, false)); occurrences = e.GetOccurrences(searchStart, searchEnd).ToList(); Assert.That(occurrences, Has.Count.EqualTo(4)); //Specifying just a date should "black out" that date var excludeTwoDaysFromNow = _now.AddDays(2).Date; - periodList.Add(new CalDateTime(excludeTwoDaysFromNow, false)); + e.ExceptionDates.Add(new CalDateTime(excludeTwoDaysFromNow, false)); occurrences = e.GetOccurrences(searchStart, searchEnd).ToList(); Assert.That(occurrences, Has.Count.EqualTo(3)); } @@ -3337,12 +3336,12 @@ public void ExDatesShouldGetMergedInOutput() }; var firstExclusion = new CalDateTime(start.AddDays(4)); - e.ExceptionDates = new List { new PeriodList() { new Period(firstExclusion) } }; + e.ExceptionDates.Add(firstExclusion); var serialized = SerializationHelpers.SerializeToString(e); Assert.That(Regex.Matches(serialized, "EXDATE:"), Has.Count.EqualTo(1)); var secondExclusion = new CalDateTime(start.AddDays(5)); - e.ExceptionDates.First().Add(new Period(secondExclusion)); + e.ExceptionDates.Add(secondExclusion); serialized = SerializationHelpers.SerializeToString(e); Assert.That(Regex.Matches(serialized, "EXDATE:"), Has.Count.EqualTo(1)); } @@ -3362,14 +3361,13 @@ public void ExDateTimeZone_Tests() RecurrenceRules = new List { rrule }, }; - var exceptionDateList = new PeriodList() { new Period(new CalDateTime(_now.AddDays(1), tzid)) }; - e.ExceptionDates.Add(exceptionDateList); + e.ExceptionDates.Add(new CalDateTime(_now.AddDays(1), tzid)); var serialized = SerializationHelpers.SerializeToString(e); const string expected = "TZID=Europe/Stockholm"; Assert.That(Regex.Matches(serialized, expected), Has.Count.EqualTo(3)); - e.ExceptionDates.First().Add(new Period(new CalDateTime(_now.AddDays(2), tzid))); + e.ExceptionDates.Add(new CalDateTime(_now.AddDays(2), tzid)); serialized = SerializationHelpers.SerializeToString(e); Assert.That(Regex.Matches(serialized, expected), Has.Count.EqualTo(3)); } @@ -3589,8 +3587,8 @@ public void ManyExclusionDatesEqualityTesting() var calendarB = collectionB.First(); var eventA = calendarA.Events.First(); var eventB = calendarB.Events.First(); - var exDatesA = eventA.ExceptionDates; - var exDatesB = eventB.ExceptionDates; + var exDatesA = eventA.ExceptionDates.GetAllDates(); + var exDatesB = eventB.ExceptionDates.GetAllDates(); Assert.Multiple(() => { diff --git a/Ical.Net.Tests/SimpleDeserializationTests.cs b/Ical.Net.Tests/SimpleDeserializationTests.cs index 64f6d59b..2c070367 100644 --- a/Ical.Net.Tests/SimpleDeserializationTests.cs +++ b/Ical.Net.Tests/SimpleDeserializationTests.cs @@ -346,36 +346,6 @@ public void Google1() Assert.That(occurrences, Has.Count.EqualTo(dateTimes.Length), "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); } - /// - /// Tests that valid RDATE properties are parsed correctly. - /// - [Test, Category("Deserialization")] - public void RecurrenceDates1() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.RecurrenceDates1)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Events.First().RecurrenceDates, Has.Count.EqualTo(3)); - - Assert.Multiple(() => - { - Assert.That(iCal.Events.First().RecurrenceDates[0][0].StartTime, Is.EqualTo(new CalDateTime(1997, 7, 14, 12, 30, 0, CalDateTime.UtcTzId))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].StartTime, Is.EqualTo(new CalDateTime(1996, 4, 3, 2, 0, 0, CalDateTime.UtcTzId))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].EndTime, Is.EqualTo(new CalDateTime(1996, 4, 3, 4, 0, 0, CalDateTime.UtcTzId))); - Assert.That(iCal.Events.First().RecurrenceDates[2][0].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][1].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 20))); - Assert.That(iCal.Events.First().RecurrenceDates[2][2].StartTime, Is.EqualTo(new CalDateTime(1997, 2, 17))); - Assert.That(iCal.Events.First().RecurrenceDates[2][3].StartTime, Is.EqualTo(new CalDateTime(1997, 4, 21))); - Assert.That(iCal.Events.First().RecurrenceDates[2][4].StartTime, Is.EqualTo(new CalDateTime(1997, 5, 26))); - Assert.That(iCal.Events.First().RecurrenceDates[2][5].StartTime, Is.EqualTo(new CalDateTime(1997, 7, 4))); - Assert.That(iCal.Events.First().RecurrenceDates[2][6].StartTime, Is.EqualTo(new CalDateTime(1997, 9, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][7].StartTime, Is.EqualTo(new CalDateTime(1997, 10, 14))); - Assert.That(iCal.Events.First().RecurrenceDates[2][8].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 28))); - Assert.That(iCal.Events.First().RecurrenceDates[2][9].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 29))); - Assert.That(iCal.Events.First().RecurrenceDates[2][10].StartTime, Is.EqualTo(new CalDateTime(1997, 12, 25))); - }); - } - - /// /// Tests that valid REQUEST-STATUS properties are parsed correctly. /// [Test, Category("Deserialization")] diff --git a/Ical.Net.Tests/VTimeZoneTest.cs b/Ical.Net.Tests/VTimeZoneTest.cs index ca14f1ec..a12b7d2b 100644 --- a/Ical.Net.Tests/VTimeZoneTest.cs +++ b/Ical.Net.Tests/VTimeZoneTest.cs @@ -5,9 +5,11 @@ using System; using System.Collections.Generic; +using System.Text.RegularExpressions; using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; using Ical.Net.Serialization; +using Ical.Net.Utility; using NUnit.Framework; namespace Ical.Net.Tests; @@ -103,7 +105,8 @@ public void VTimeZoneEuropeMoscowShouldSerializeProperly() { var iCal = CreateTestCalendar("Europe/Moscow"); var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); + // Unwrap the lines to make it easier to search for specific values + var serialized = TextUtil.UnwrapLines(serializer.SerializeToString(iCal)); Assert.Multiple(() => { @@ -122,7 +125,8 @@ public void VTimeZoneEuropeMoscowShouldSerializeProperly() Assert.That(serialized.Contains("TZOFFSETTO:+023017"), Is.True, "TZOFFSETTO:+023017 was not serialized"); Assert.That(serialized.Contains("DTSTART:19180916T010000"), Is.True, "DTSTART:19180916T010000 was not serialized"); Assert.That(serialized.Contains("DTSTART:19171228T000000"), Is.True, "DTSTART:19171228T000000 was not serialized"); - Assert.That(serialized.Contains("RDATE:19991031T030000"), Is.True, "RDATE:19991031T030000 was not serialized"); + // RDATE may contain multiple dates, separated by a comma + Assert.That(Regex.IsMatch(serialized, $@"RDATE:.*\b19991031T030000\b", RegexOptions.Compiled, RegexDefaults.Timeout), Is.True, "RDATE:19731028T020000 was not serialized"); }); } @@ -201,7 +205,8 @@ public void VTimeZoneAmericaAnchorageShouldSerializeProperly() { var iCal = CreateTestCalendar("America/Anchorage"); var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); + // Unwrap the lines to make it easier to search for specific values + var serialized = TextUtil.UnwrapLines(serializer.SerializeToString(iCal)); Assert.Multiple(() => { @@ -214,10 +219,11 @@ public void VTimeZoneAmericaAnchorageShouldSerializeProperly() Assert.That(serialized.Contains("TZNAME:YST"), Is.True, "YST was not serialized"); Assert.That(serialized.Contains("TZNAME:AHDT"), Is.True, "AHDT was not serialized"); Assert.That(serialized.Contains("TZNAME:LMT"), Is.True, "LMT was not serialized"); - Assert.That(serialized.Contains("RDATE:19731028T020000"), Is.True, "RDATE:19731028T020000 was not serialized"); - Assert.That(serialized.Contains("RDATE:19801026T020000"), Is.True, "RDATE:19801026T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19420209T020000"), Is.True, "DTSTART:19420209T020000 was not serialized"); + // RDATE may contain multiple dates, separated by a comma + Assert.That(Regex.IsMatch(serialized, $@"RDATE:.*\b19731028T020000\b", RegexOptions.Compiled, RegexDefaults.Timeout), Is.True, "RDATE:19731028T020000 was not serialized"); + Assert.That(Regex.IsMatch(serialized, $@"RDATE:.*\b19801026T020000\b", RegexOptions.Compiled, RegexDefaults.Timeout), Is.True, "RDATE:19731028T020000 was not serialized"); Assert.That(serialized.Contains("RDATE:19670401/P1D"), Is.False, "RDate was not properly serialized for vtimezone, should be RDATE:19670401T000000"); + Assert.That(serialized.Contains("DTSTART:19420209T020000"), Is.True, "DTSTART:19420209T020000 was not serialized"); }); } @@ -294,4 +300,4 @@ private static Calendar CreateTestCalendar(string tzId, DateTime? earliestTime = iCal.Events.Add(calEvent2); return iCal; } -} \ No newline at end of file +} diff --git a/Ical.Net/CalendarComponents/CalendarEvent.cs b/Ical.Net/CalendarComponents/CalendarEvent.cs index 0e42be53..9c85ab99 100644 --- a/Ical.Net/CalendarComponents/CalendarEvent.cs +++ b/Ical.Net/CalendarComponents/CalendarEvent.cs @@ -288,8 +288,8 @@ protected bool Equals(CalendarEvent? other) // RDATEs and EXDATEs are all List, because the spec allows for multiple declarations of collections. // Consequently we have to contrive a normalized representation before we can determine whether two events are equal - var exDates = PeriodList.GetGroupedPeriods(ExceptionDates); - var otherExDates = PeriodList.GetGroupedPeriods(other.ExceptionDates); + var exDates = PeriodList.GetGroupedPeriods(ExceptionDatesPeriodLists); + var otherExDates = PeriodList.GetGroupedPeriods(other.ExceptionDatesPeriodLists); if (exDates.Keys.Count != otherExDates.Keys.Count || !exDates.Keys.OrderBy(k => k).SequenceEqual(otherExDates.Keys.OrderBy(k => k))) { return false; @@ -300,8 +300,8 @@ protected bool Equals(CalendarEvent? other) return false; } - var rDates = PeriodList.GetGroupedPeriods(RecurrenceDates); - var otherRDates = PeriodList.GetGroupedPeriods(other.RecurrenceDates); + var rDates = PeriodList.GetGroupedPeriods(RecurrenceDatesPeriodLists); + var otherRDates = PeriodList.GetGroupedPeriods(other.RecurrenceDatesPeriodLists); if (rDates.Keys.Count != otherRDates.Keys.Count || !rDates.Keys.OrderBy(k => k).SequenceEqual(otherRDates.Keys.OrderBy(k => k))) { return false; @@ -339,9 +339,9 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ Transparency?.GetHashCode() ?? 0; hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Resources); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(ExceptionDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(ExceptionDatesPeriodLists); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(RecurrenceDatesPeriodLists); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); return hashCode; } diff --git a/Ical.Net/CalendarComponents/IRecurrable.cs b/Ical.Net/CalendarComponents/IRecurrable.cs index 31d24215..7b58df39 100644 --- a/Ical.Net/CalendarComponents/IRecurrable.cs +++ b/Ical.Net/CalendarComponents/IRecurrable.cs @@ -15,9 +15,9 @@ public interface IRecurrable : IGetOccurrences, IServiceProvider /// IDateTime Start { get; set; } - IList ExceptionDates { get; set; } + ExceptionDates ExceptionDates { get; } IList ExceptionRules { get; set; } - IList RecurrenceDates { get; set; } + RecurrenceDates RecurrenceDates { get; } IList RecurrenceRules { get; set; } IDateTime RecurrenceId { get; set; } -} \ No newline at end of file +} diff --git a/Ical.Net/CalendarComponents/RecurringComponent.cs b/Ical.Net/CalendarComponents/RecurringComponent.cs index 2e6dec20..50323e98 100644 --- a/Ical.Net/CalendarComponents/RecurringComponent.cs +++ b/Ical.Net/CalendarComponents/RecurringComponent.cs @@ -75,12 +75,14 @@ public virtual IDateTime DtStart set => Properties.Set("DTSTART", value); } - public virtual IList ExceptionDates + internal IList ExceptionDatesPeriodLists { get => Properties.GetMany("EXDATE"); set => Properties.Set("EXDATE", value); } + public virtual ExceptionDates ExceptionDates { get; internal set; } + public virtual IList ExceptionRules { get => Properties.GetMany("EXRULE"); @@ -99,12 +101,14 @@ public virtual int Priority set => Properties.Set("PRIORITY", value); } - public virtual IList RecurrenceDates + internal virtual IList RecurrenceDatesPeriodLists { get => Properties.GetMany("RDATE"); set => Properties.Set("RDATE", value); } + public virtual RecurrenceDates RecurrenceDates { get; internal set; } + public virtual IList RecurrenceRules { get => Properties.GetMany("RRULE"); @@ -161,7 +165,12 @@ public RecurringComponent(string name) : base(name) EnsureProperties(); } - private void Initialize() => SetService(new RecurringEvaluator(this)); + private void Initialize() + { + SetService(new RecurringEvaluator(this)); + ExceptionDates = new ExceptionDates(ExceptionDatesPeriodLists); + RecurrenceDates = new RecurrenceDates(RecurrenceDatesPeriodLists); + } private void EnsureProperties() { @@ -201,9 +210,9 @@ protected bool Equals(RecurringComponent other) && Attachments.SequenceEqual(other.Attachments) && CollectionHelpers.Equals(Categories, other.Categories) && CollectionHelpers.Equals(Contacts, other.Contacts) - && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) + && CollectionHelpers.Equals(ExceptionDatesPeriodLists, other.ExceptionDatesPeriodLists) && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) - && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, orderSignificant: true) + && CollectionHelpers.Equals(RecurrenceDatesPeriodLists, other.RecurrenceDatesPeriodLists, orderSignificant: true) && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, orderSignificant: true); return result; @@ -229,9 +238,9 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Categories); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Contacts); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDatesPeriodLists); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDatesPeriodLists); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); return hashCode; } diff --git a/Ical.Net/CalendarComponents/VTimeZone.cs b/Ical.Net/CalendarComponents/VTimeZone.cs index 5e37e6fb..749ff234 100644 --- a/Ical.Net/CalendarComponents/VTimeZone.cs +++ b/Ical.Net/CalendarComponents/VTimeZone.cs @@ -241,7 +241,6 @@ private static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List< { foreach (var interval in intervals) { - var periodList = new PeriodList(); var time = interval.IsoLocalStart.ToDateTimeUnspecified(); var date = new CalDateTime(time, true).Add(delta.ToDurationExact()) as CalDateTime; if (date == null) @@ -249,8 +248,7 @@ private static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List< continue; } - periodList.Add(date); - tzi.RecurrenceDates.Add(periodList); + tzi.RecurrenceDates.Add(date); } } diff --git a/Ical.Net/DataTypes/PeriodList.cs b/Ical.Net/DataTypes/PeriodList.cs index 7698e624..bd03fcff 100644 --- a/Ical.Net/DataTypes/PeriodList.cs +++ b/Ical.Net/DataTypes/PeriodList.cs @@ -19,7 +19,7 @@ namespace Ical.Net.DataTypes; /// An iCalendar list used to represent a list of objects /// for EXDATE and RDATE properties. /// -public class PeriodList : EncodableDataType, IList +internal class PeriodList : EncodableDataType, IList { internal PeriodKind PeriodKind => Count == 0 ? PeriodKind.Undefined : Periods[0].PeriodKind; diff --git a/Ical.Net/DataTypes/PeriodListWrapperBase.cs b/Ical.Net/DataTypes/PeriodListWrapperBase.cs index f4a600ac..7a09c56f 100644 --- a/Ical.Net/DataTypes/PeriodListWrapperBase.cs +++ b/Ical.Net/DataTypes/PeriodListWrapperBase.cs @@ -17,7 +17,7 @@ namespace Ical.Net.DataTypes; /// public abstract class PeriodListWrapperBase { - protected IList ListOfPeriodList; + private protected IList ListOfPeriodList; private protected PeriodListWrapperBase(IList periodList) => ListOfPeriodList = periodList; @@ -55,7 +55,7 @@ public bool Remove(IDateTime dt) return periodList.Remove(dtPeriod); } - protected PeriodList GetOrCreatePeriodList(IDateTime dt) + private protected PeriodList GetOrCreatePeriodList(IDateTime dt) { var periodList = GetPeriodList(dt); @@ -66,7 +66,7 @@ protected PeriodList GetOrCreatePeriodList(IDateTime dt) return periodList; } - protected PeriodList GetOrCreatePeriodList(Period period) + private protected PeriodList GetOrCreatePeriodList(Period period) { var periodList = GetPeriodList(period); @@ -77,7 +77,7 @@ protected PeriodList GetOrCreatePeriodList(Period period) return periodList; } - protected PeriodList? GetPeriodList(IDateTime dt) + private protected PeriodList? GetPeriodList(IDateTime dt) { // The number of PeriodLists is expected to be small, so a linear search is acceptable. return ListOfPeriodList @@ -86,7 +86,7 @@ protected PeriodList GetOrCreatePeriodList(Period period) && p.PeriodKind == (dt.HasTime ? PeriodKind.DateTime : PeriodKind.DateOnly)); } - protected PeriodList? GetPeriodList(Period period) + private protected PeriodList? GetPeriodList(Period period) { // The number of PeriodLists is expected to be small, so a linear search is acceptable. return ListOfPeriodList diff --git a/Ical.Net/Evaluation/PeriodListEvaluator.cs b/Ical.Net/Evaluation/PeriodListEvaluator.cs index bae66048..d90585fe 100644 --- a/Ical.Net/Evaluation/PeriodListEvaluator.cs +++ b/Ical.Net/Evaluation/PeriodListEvaluator.cs @@ -9,7 +9,7 @@ namespace Ical.Net.Evaluation; -public class PeriodListEvaluator : Evaluator +internal class PeriodListEvaluator : Evaluator { private readonly PeriodList _mPeriodList; @@ -24,7 +24,7 @@ public override IEnumerable Evaluate(IDateTime referenceDate, DateTime? if (includeReferenceDateInResults) { - Period p = new Period(referenceDate); + var p = new Period(referenceDate); periods.Add(p); } diff --git a/Ical.Net/Evaluation/RecurringEvaluator.cs b/Ical.Net/Evaluation/RecurringEvaluator.cs index 0509d10e..6886df32 100644 --- a/Ical.Net/Evaluation/RecurringEvaluator.cs +++ b/Ical.Net/Evaluation/RecurringEvaluator.cs @@ -73,10 +73,9 @@ protected IEnumerable EvaluateRRule(IDateTime referenceDate, DateTime? p /// Evaluates the RDate component. protected IEnumerable EvaluateRDate(IDateTime referenceDate, DateTime? periodStart, DateTime? periodEnd) { - if (Recurrable.RecurrenceDates == null || !Recurrable.RecurrenceDates.Any()) - return []; + var recurrences = new SortedSet(Recurrable.RecurrenceDates.GetAllPeriods() + .Union(Recurrable.RecurrenceDates.GetAllDates().Select(d => new Period(d)))); - var recurrences = new SortedSet(Recurrable.RecurrenceDates.SelectMany(rdate => rdate)); return recurrences; } @@ -116,10 +115,7 @@ protected IEnumerable EvaluateExRule(IDateTime referenceDate, DateTime? /// The end date of the range to evaluate. protected IEnumerable EvaluateExDate(IDateTime referenceDate, DateTime? periodStart, DateTime? periodEnd) { - if (Recurrable.ExceptionDates == null || !Recurrable.ExceptionDates.Any()) - return []; - - var exDates = new SortedSet(Recurrable.ExceptionDates.SelectMany(exDate => exDate)); + var exDates = new SortedSet(Recurrable.ExceptionDates.GetAllDates().Select(exDate => new Period(exDate))); return exDates; } diff --git a/Ical.Net/Evaluation/TodoEvaluator.cs b/Ical.Net/Evaluation/TodoEvaluator.cs index 24cc4d09..c7e38d7d 100644 --- a/Ical.Net/Evaluation/TodoEvaluator.cs +++ b/Ical.Net/Evaluation/TodoEvaluator.cs @@ -29,13 +29,10 @@ internal IEnumerable EvaluateToPreviousOccurrence(IDateTime completedDat DetermineStartingRecurrence(rrule, ref beginningDate); } } - if (Todo.RecurrenceDates != null) - { - foreach (var rdate in Todo.RecurrenceDates) - { - DetermineStartingRecurrence(rdate, ref beginningDate); - } - } + + DetermineStartingRecurrence(Todo.RecurrenceDates.GetAllPeriods(), ref beginningDate); + DetermineStartingRecurrence(Todo.RecurrenceDates.GetAllDates(), ref beginningDate); + if (Todo.ExceptionRules != null) { foreach (var exrule in Todo.ExceptionRules) @@ -43,18 +40,13 @@ internal IEnumerable EvaluateToPreviousOccurrence(IDateTime completedDat DetermineStartingRecurrence(exrule, ref beginningDate); } } - if (Todo.ExceptionDates != null) - { - foreach (var exdate in Todo.ExceptionDates) - { - DetermineStartingRecurrence(exdate, ref beginningDate); - } - } + + DetermineStartingRecurrence(Todo.ExceptionDates.GetAllDates(), ref beginningDate); return Evaluate(Todo.Start, DateUtil.GetSimpleDateTimeData(beginningDate), DateUtil.GetSimpleDateTimeData(currDt).AddTicks(1), true); } - private void DetermineStartingRecurrence(PeriodList rdate, ref IDateTime referenceDateTime) + private static void DetermineStartingRecurrence(IEnumerable rdate, ref IDateTime referenceDateTime) { var dt2 = referenceDateTime; foreach (var p in rdate.Where(p => p.StartTime.LessThan(dt2))) @@ -63,6 +55,15 @@ private void DetermineStartingRecurrence(PeriodList rdate, ref IDateTime referen } } + private static void DetermineStartingRecurrence(IEnumerable rdate, ref IDateTime referenceDateTime) + { + var dt2 = referenceDateTime; + foreach (var dt in rdate.Where(dt => dt.LessThan(dt2))) + { + referenceDateTime = dt; + } + } + private void DetermineStartingRecurrence(RecurrencePattern recur, ref IDateTime referenceDateTime) { if (recur.Count.HasValue) diff --git a/Ical.Net/VTimeZoneInfo.cs b/Ical.Net/VTimeZoneInfo.cs index 3cfa12ed..b5dae7e3 100644 --- a/Ical.Net/VTimeZoneInfo.cs +++ b/Ical.Net/VTimeZoneInfo.cs @@ -29,12 +29,15 @@ public VTimeZoneInfo() public VTimeZoneInfo(string name) : this() { Name = name; + Initialize(); } private void Initialize() { _evaluator = new TimeZoneInfoEvaluator(this); SetService(_evaluator); + ExceptionDates = new ExceptionDates(ExceptionDatesPeriodLists); + RecurrenceDates = new RecurrenceDates(RecurrenceDatesPeriodLists); } protected override void OnDeserializing(StreamingContext context) @@ -142,24 +145,28 @@ public virtual IDateTime Start set => Properties.Set("DTSTART", value); } - public virtual IList ExceptionDates + internal IList ExceptionDatesPeriodLists { get => Properties.GetMany("EXDATE"); set => Properties.Set("EXDATE", value); } + public virtual ExceptionDates ExceptionDates { get; private set; } + public virtual IList ExceptionRules { get => Properties.GetMany("EXRULE"); set => Properties.Set("EXRULE", value); } - public virtual IList RecurrenceDates + internal IList RecurrenceDatesPeriodLists { get => Properties.GetMany("RDATE"); set => Properties.Set("RDATE", value); } + public virtual RecurrenceDates RecurrenceDates { get; private set; } + public virtual IList RecurrenceRules { get => Properties.GetMany("RRULE");