Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Ical.Net.Tests/Calendars/Alarm/ALARM4.ics
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ DTSTAMP:20060717T210718Z
UID:uuid1153170430406
SUMMARY:Test event
DTSTART;TZID=US-Eastern:19970902T090000
DTEND;TZID=US-Eastern:19970902T100000
EXDATE;TZID=US-Eastern:19970902T090000
RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
BEGIN:VALARM
Expand Down
1 change: 0 additions & 1 deletion Ical.Net.Tests/Calendars/Alarm/ALARM6.ics
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ DTSTAMP:20060717T210718Z
UID:uuid1153170430406
SUMMARY:Test event
DTSTART;TZID=US-Eastern:19970902T090000
DTEND;TZID=US-Eastern:19970902T100000
EXDATE;TZID=US-Eastern:19970902T090000
RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
BEGIN:VALARM
Expand Down
1 change: 0 additions & 1 deletion Ical.Net.Tests/Calendars/Recurrence/DailyInterval1.ics
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ CREATED:20070405T013153Z
DTEND;TZID=US-Eastern:20070410T070000
DTSTAMP:20070405T013153Z
DTSTART;TZID=US-Eastern:20070409T070000
DURATION:P1D
RRULE:FREQ=DAILY;INTERVAL=3
SUMMARY:Every third day
UID:baff9b6a-a161-4057-bd9c-d76359dc90b1
Expand Down
2 changes: 1 addition & 1 deletion Ical.Net.Tests/Calendars/Recurrence/DailyInterval2.ics
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN
BEGIN:VEVENT
DTEND:20070410T070000
DURATION:P1D
DTSTART:20070409T070000
RRULE:FREQ=DAILY;INTERVAL=2
SUMMARY:Every 2 days
Expand Down
1 change: 0 additions & 1 deletion Ical.Net.Tests/Calendars/Recurrence/HourlyInterval1.ics
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ CREATED:20070405T013153Z
DTEND;TZID=US-Eastern:20070410T070000
DTSTAMP:20070405T013153Z
DTSTART;TZID=US-Eastern:20070409T070000
DURATION:P1D
RRULE:FREQ=HOURLY;INTERVAL=18
SUMMARY:Every 18 hours
UID:baff9b6a-a161-4057-bd9c-d76359dc90b1
Expand Down
1 change: 0 additions & 1 deletion Ical.Net.Tests/Calendars/Recurrence/MonthlyInterval1.ics
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ CREATED:20070405T013153Z
DTEND;TZID=US-Eastern:20070410T070000
DTSTAMP:20070405T013153Z
DTSTART;TZID=US-Eastern:20070409T070000
DURATION:P1D
RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=MO,TU;BYMONTHDAY=8,9,10,11,12,13,14
SUMMARY:Every other month, on Monday and Tuesday on the 2nd week of the month.
UID:baff9b6a-a161-4057-bd9c-d76359dc90b1
Expand Down
1 change: 0 additions & 1 deletion Ical.Net.Tests/Calendars/Recurrence/YearlyInterval1.ics
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ CREATED:20050405T013153Z
DTEND;TZID=US-Eastern:20050410T070000
DTSTAMP:20050405T013153Z
DTSTART;TZID=US-Eastern:20050409T070000
DURATION:P1D
RRULE:FREQ=YEARLY;INTERVAL=2;BYDAY=MO,TU;BYMONTHDAY=8,9,10,11,12,13,14
SUMMARY:Every other year, on Monday and Tuesday on the 2nd week of each month.
UID:baff9b6a-a161-4057-bd9c-d76359dc90b1
Expand Down
3 changes: 2 additions & 1 deletion Ical.Net.Tests/DeserializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ public void Bug2938007()
Assert.Multiple(() =>
{
Assert.That(o.Period.StartTime.HasTime, Is.EqualTo(true));
Assert.That(o.Period.EndTime.HasTime, Is.EqualTo(true));
Assert.That(o.Period.EndTime, Is.Null);
Assert.That(o.Period.EffectiveEndTime, Is.Not.Null);
});
}
}
Expand Down
4 changes: 2 additions & 2 deletions Ical.Net.Tests/GetOccurrenceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void WrongDurationTest()
Assert.Multiple(() =>
{
Assert.That(firstOccurrence.Period.StartTime, Is.EqualTo(firstStartCopy));
Assert.That(firstOccurrence.Period.EndTime, Is.EqualTo(firstEndCopy));
Assert.That(firstOccurrence.Period.EffectiveEndTime, Is.EqualTo(firstEndCopy));
});

var secondOccurrence = occurrences.Last();
Expand All @@ -52,7 +52,7 @@ public void WrongDurationTest()
Assert.Multiple(() =>
{
Assert.That(secondOccurrence.Period.StartTime, Is.EqualTo(secondStartCopy));
Assert.That(secondOccurrence.Period.EndTime, Is.EqualTo(secondEndCopy));
Assert.That(secondOccurrence.Period.EffectiveEndTime, Is.EqualTo(secondEndCopy));
});
}

Expand Down
15 changes: 0 additions & 15 deletions Ical.Net.Tests/PeriodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,4 @@ public void Timezones_StartTime_EndTime_MustBeEqual()
}
});
}

[Test]
public void CollidesWithPeriod()
{
var period1 = new Period(new CalDateTime(2025, 1, 1, 0, 0, 0), Duration.FromHours(1));
var period2 = new Period(new CalDateTime(2025, 1, 1, 0, 30, 0), Duration.FromHours(1));
var period3 = new Period(new CalDateTime(2025, 1, 1, 1, 30, 0), Duration.FromHours(1));

Assert.Multiple(() =>
{
Assert.That(period1.CollidesWith(period2), Is.True);
Assert.That(period1.CollidesWith(period3), Is.False);
Assert.That(period2.CollidesWith(period3), Is.True);
});
}
}
61 changes: 47 additions & 14 deletions Ical.Net.Tests/RecurrenceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ int eventIndex

for (var i = 0; i < expectedPeriods.Length; i++)
{
var period = new Period(expectedPeriods[i].StartTime, expectedPeriods[i].EffectiveEndTime);
var period = new Period(expectedPeriods[i].StartTime, expectedPeriods[i].EffectiveDuration!.Value);

Assert.That(occurrences[i].Period, Is.EqualTo(period), "Event should occur on " + period);
if (timeZones != null)
Expand Down Expand Up @@ -2539,14 +2539,14 @@ public void Bug3007244()
// specified via DTEND: exact
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DTEND;TZID=Europe/Vienna:20241021T040000", "20241020T010000/PT27H", "20241027T010000/PT27H", "20241103T010000/PT27H")]
// First days are applied nominal, then time exact
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DURATION:P1DT3H", "20241020T010000/PT27H", "20241027T010000/PT28H", "20241103T010000/PT27H")]
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DURATION:P1DT3H", "20241020T010000/P1DT3H", "20241027T010000/P1DT3H", "20241103T010000/P1DT3H")]
// Exact, because duration is time-only
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DURATION:PT27H", "20241020T010000/PT27H", "20241027T010000/PT27H", "20241103T010000/PT27H")]

// specified via DTEND: exact
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DTEND;TZID=Europe/Vienna:20241027T040000", "20241020T010000/PT172H", "20241027T010000/PT172H", "20241103T010000/PT172H")]
// First days are applied nominal, then time exact
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DURATION:P7DT3H", "20241020T010000/PT171H", "20241027T010000/PT172H", "20241103T010000/PT171H")]
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DURATION:P7DT3H", "20241020T010000/P7DT3H", "20241027T010000/P7DT3H", "20241103T010000/P7DT3H")]
// Exact, because duration is time-only
[TestCase("DTSTART;TZID=Europe/Vienna:20241020T010000", "DURATION:PT171H", "20241020T010000/PT171H", "20241027T010000/PT171H", "20241103T010000/PT171H")]

Expand Down Expand Up @@ -2575,8 +2575,8 @@ public void Bug3007244()
//
// see https://github.com/ical-org/ical.net/issues/681
[TestCase("DTSTART;TZID=Europe/Vienna:20250316T023000", "DTEND;TZID=Europe/Vienna:20250323T023000", "20250316T023000/PT168H", "20250323T023000/PT168H", "20250330T033000/PT168H")]
[TestCase("DTSTART;TZID=Europe/Vienna:20250316T023000", "DURATION:P1W", "20250316T023000/PT168H", "20250323T023000/PT168H", "20250330T033000/PT168H")]
[TestCase("DTSTART;TZID=Europe/Vienna:20250316T023000", "DURATION:P7D", "20250316T023000/PT168H", "20250323T023000/PT168H", "20250330T033000/PT168H")]
[TestCase("DTSTART;TZID=Europe/Vienna:20250316T023000", "DURATION:P1W", "20250316T023000/P1W", "20250323T023000/P1W", "20250330T033000/P1W")]
[TestCase("DTSTART;TZID=Europe/Vienna:20250316T023000", "DURATION:P7D", "20250316T023000/P7D", "20250323T023000/P7D", "20250330T033000/P7D")]

public void DurationOfRecurrencesOverDst(string dtStart, string dtEnd, string? d1, string? d2, string? d3)
{
Expand All @@ -2603,7 +2603,7 @@ public void DurationOfRecurrencesOverDst(string dtStart, string dtEnd, string? d
{
var p = expectedPeriods[index];
var newStart = p.StartTime.ToTimeZone(start!.TzId);
expectedPeriods[index] = Period.Create(newStart, end: newStart.Add(p.Duration!.Value));
expectedPeriods[index] = Period.Create(newStart, duration: p.EffectiveDuration);
}

// date only cannot have a time zone
Expand Down Expand Up @@ -3097,7 +3097,7 @@ public void Test4()
RDATE;TZID=America/Chicago:99991231T221000
END:VEVENT
END:VCALENDAR
""")]
""", true)]

// y10k exceeded due to the event duration
[TestCase("""
Expand All @@ -3108,7 +3108,7 @@ public void Test4()
RRULE:FREQ=DAILY;BYHOUR=22,23;COUNT=2
END:VEVENT
END:VCALENDAR
""")]
""", false)]

// Events are merged in different places than individual RRULES of a single event
[TestCase("""
Expand All @@ -3120,11 +3120,11 @@ public void Test4()
DTSTART;TZID=America/Chicago:99991231T221000
END:VEVENT
END:VCALENDAR
""")]
public void Recurrence_WithOutOfBoundsUtc_ShouldFailWithCorrectException(string ical)
""", true)]
public void Recurrence_WithOutOfBoundsUtc_ShouldFailWithCorrectException(string ical, bool shouldThrow)
{
var cal = Calendar.Load(ical)!;
Assert.That(() => cal.GetOccurrences().ToList(), Throws.InstanceOf<EvaluationOutOfRangeException>());
Assert.That(() => cal.GetOccurrences().ToList(), shouldThrow ? Throws.InstanceOf<EvaluationOutOfRangeException>() : Throws.Nothing);
}

[Test, Category("Recurrence")]
Expand Down Expand Up @@ -3327,15 +3327,15 @@ public void EventsWithShareUidsShouldGenerateASingleRecurrenceSet()
Assert.Multiple(() =>
{
Assert.That(orderedOccurrences[3].StartTime, Is.EqualTo(expectedSept1Start));
Assert.That(orderedOccurrences[3].EndTime, Is.EqualTo(expectedSept1End));
Assert.That(orderedOccurrences[3].EffectiveEndTime, Is.EqualTo(expectedSept1End));
});

var expectedSept3Start = new CalDateTime(DateTime.Parse("2016-09-03T07:00:00", CultureInfo.InvariantCulture), "Europe/Bucharest");
var expectedSept3End = new CalDateTime(DateTime.Parse("2016-09-03T12:30:00", CultureInfo.InvariantCulture), "Europe/Bucharest");
Assert.Multiple(() =>
{
Assert.That(orderedOccurrences[5].StartTime, Is.EqualTo(expectedSept3Start));
Assert.That(orderedOccurrences[5].EndTime, Is.EqualTo(expectedSept3End));
Assert.That(orderedOccurrences[5].EffectiveEndTime, Is.EqualTo(expectedSept3End));
});
}

Expand Down Expand Up @@ -3790,7 +3790,7 @@ Type LoadType(string name) =>

if (testCase.ExceptionStep == RecurrenceTestExceptionStep.Enumeration)
{
Assert.That(() => EnumerateOccurrences(), throwsConstraint);
Assert.That(() => EnumerateOccurrences().Last().Period.EffectiveEndTime, throwsConstraint);
return;
}

Expand Down Expand Up @@ -4031,4 +4031,37 @@ public void Disallowed_Recurrence_RangeChecks_Should_Throw()
Assert.That(() => serializer.CheckRange("a", (int?) 0, 1, 2, false), Throws.TypeOf<ArgumentOutOfRangeException>());
});
}

[Test]
public void AmbiguousLocalTime_WithShortDurationOfRecurrence()
{
// Short recurrence falls into an ambiguous local time
// for the end time of the second occurrence because
// of DST transition on 2025-10-25 03:00
// See also: https://github.com/ical-org/ical.net/issues/737
var ics = """
BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART;TZID=Europe/Vienna:20201024T023000
DURATION:PT45M
RRULE:FREQ=DAILY;UNTIL=20201025T013000Z
END:VEVENT
END:VCALENDAR
""";
var cal = Calendar.Load(ics)!;
var occ = cal.GetOccurrences().ToList();

Assert.Multiple(() =>
{
Assert.That(occ.Count, Is.EqualTo(2));

Assert.That(occ[0].Period.StartTime, Is.EqualTo(new CalDateTime(2020, 10, 24, 2, 30, 0, "Europe/Vienna")));
Assert.That(occ[0].Period.EffectiveEndTime, Is.EqualTo(new CalDateTime(2020, 10, 24, 3, 15, 0, "Europe/Vienna")));
Assert.That(occ[0].Period.EffectiveDuration, Is.EqualTo(new Duration(0, 0, 0, 45, 0)));

Assert.That(occ[1].Period.StartTime, Is.EqualTo(new CalDateTime(2020, 10, 25, 2, 30, 0, "Europe/Vienna")));
Assert.That(occ[1].Period.EffectiveEndTime, Is.EqualTo(new CalDateTime(2020, 10, 25, 2, 15, 0, "Europe/Vienna")));
Assert.That(occ[1].Period.EffectiveDuration, Is.EqualTo(new Duration(0, 0, 0, 45, 0)));
});
}
}
20 changes: 10 additions & 10 deletions Ical.Net.Tests/RecurrenceTests_From_Issues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public void ClockGoingForwardTest()
Assert.That(occurrences.Count(), Is.EqualTo(1));
Assert.That(occurrence.Source, Is.SameAs(myEvent));
Assert.That(occurrence.Period.StartTime, Is.EqualTo(myEvent.Start));
Assert.That(occurrence.Period.EndTime, Is.EqualTo(myEvent.End));
Assert.That(occurrence.Period.EffectiveEndTime, Is.EqualTo(myEvent.End));
});
}

Expand Down Expand Up @@ -119,7 +119,7 @@ public void ClockGoingBackTest()
Assert.That(occurrences.Count(), Is.EqualTo(1));
Assert.That(occurrence.Source, Is.SameAs(myEvent));
Assert.That(occurrence.Period.StartTime, Is.EqualTo(myEvent.Start));
Assert.That(occurrence.Period.EndTime, Is.EqualTo(myEvent.End));
Assert.That(occurrence.Period.EffectiveEndTime, Is.EqualTo(myEvent.End));
});
}

Expand Down Expand Up @@ -156,8 +156,8 @@ public void ClockGoingForwardAllDayTest()
Assert.That(occurrence.Source, Is.SameAs(myEvent));
Assert.That(occurrence.Period.StartTime.HasTime, Is.False);
Assert.That(occurrence.Period.StartTime, Is.EqualTo(myEvent.Start));
Assert.That(occurrence.Period.EndTime?.HasTime, Is.False);
Assert.That(occurrence.Period.EndTime, Is.EqualTo(myEvent.End));
Assert.That(occurrence.Period.EndTime, Is.Null);
Assert.That(occurrence.Period.EffectiveEndTime, Is.EqualTo(myEvent.End));
});
}

Expand Down Expand Up @@ -195,8 +195,8 @@ public void ClockGoingBackAllDayTest()
Assert.That(occurrence.Source, Is.SameAs(myEvent));
Assert.That(occurrence.Period.StartTime.HasTime, Is.False);
Assert.That(occurrence.Period.StartTime, Is.EqualTo(myEvent.Start));
Assert.That(occurrence.Period.EndTime?.HasTime, Is.False);
Assert.That(occurrence.Period.EndTime, Is.EqualTo(myEvent.End));
Assert.That(occurrence.Period.EndTime, Is.Null);
Assert.That(occurrence.Period.EffectiveEndTime, Is.EqualTo(myEvent.End));
});
}

Expand Down Expand Up @@ -233,8 +233,8 @@ public void ClockGoingBackAllDayNonLocalTest()
Assert.That(occurrence.Source, Is.SameAs(myEvent));
Assert.That(occurrence.Period.StartTime.HasTime, Is.False);
Assert.That(occurrence.Period.StartTime, Is.EqualTo(myEvent.Start));
Assert.That(occurrence.Period.EndTime?.HasTime, Is.False);
Assert.That(occurrence.Period.EndTime, Is.EqualTo(myEvent.End));
Assert.That(occurrence.Period.EndTime, Is.Null);
Assert.That(occurrence.Period.EffectiveEndTime, Is.EqualTo(myEvent.End));
});
}

Expand Down Expand Up @@ -271,8 +271,8 @@ public void ClockGoingForwardAllDayNonLocalTest()
Assert.That(myEvent.IsAllDay, Is.True);
Assert.That(occurrence.Period.StartTime.HasTime, Is.False);
Assert.That(occurrence.Period.StartTime, Is.EqualTo(myEvent.Start));
Assert.That(occurrence.Period.EndTime?.HasTime, Is.False);
Assert.That(occurrence.Period.EndTime, Is.EqualTo(myEvent.End));
Assert.That(occurrence.Period.EndTime, Is.Null);
Assert.That(occurrence.Period.EffectiveEndTime, Is.EqualTo(myEvent.End));
});
}

Expand Down
2 changes: 1 addition & 1 deletion Ical.Net.Tests/RecurrenceWithExDateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void ShouldNotOccurOnLocalExceptionDate(bool useExDateWithTime)
{
if (useExDateWithTime)
{
Assert.That(occurrences.Single().Period, Is.EqualTo(new Period(start, end)));
Assert.That(occurrences.Single().Period, Is.EqualTo(new Period(start, end.Subtract(start))));
Assert.That(ics, Does.Contain("EXDATE;TZID=Europe/London:20241019T210000"));
}
else
Expand Down
2 changes: 1 addition & 1 deletion Ical.Net.Tests/RecurrenceWithRDateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,6 @@ public void RDate_DateOnly_WithExactDuration_ShouldThrow()
};
calendarEvent.RecurrenceDates.AddRange(recurrenceDates);

Assert.That(() => { _ = calendarEvent.GetOccurrences().ToList(); }, Throws.InvalidOperationException);
Assert.That(() => { _ = calendarEvent.GetOccurrences().ToList(); }, Throws.ArgumentException);
}
}
7 changes: 4 additions & 3 deletions Ical.Net.Tests/SimpleDeserializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,17 @@ public void Bug2938007()
var evt = iCal.Events.First();
Assert.Multiple(() =>
{
Assert.That(evt.Start.HasTime, Is.EqualTo(true));
Assert.That(evt.End.HasTime, Is.EqualTo(true));
Assert.That(evt.Start.HasTime, Is.True);
Assert.That(evt.End.HasTime, Is.True);
});

foreach (var o in evt.GetOccurrences(new CalDateTime(2010, 1, 17, 0, 0, 0)).TakeWhileBefore(new CalDateTime(2010, 2, 1, 0, 0, 0)))
{
Assert.Multiple(() =>
{
Assert.That(o.Period.StartTime.HasTime, Is.EqualTo(true));
Assert.That(o.Period.EndTime.HasTime, Is.EqualTo(true));
Assert.That(o.Period.EndTime, Is.Null);
Assert.That(o.Period.EffectiveEndTime, Is.Not.Null);
});
}
}
Expand Down
Loading
Loading