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
18 changes: 12 additions & 6 deletions Ical.Net.Benchmarks/ApplicationWorkflows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using BenchmarkDotNet.Attributes;
using Ical.Net.DataTypes;
using NodaTime;
using System;
using System.Collections.Generic;
using System.IO;
Expand All @@ -14,8 +15,13 @@ namespace Ical.Net.Benchmarks;

public class ApplicationWorkflows
{
private static readonly CalDateTime _searchStart = CalDateTime.Now.AddDays(-365);
private static readonly CalDateTime _searchEnd = CalDateTime.Now;
private static DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];

private static readonly Instant _searchEnd = SystemClock.Instance.GetCurrentInstant();
private static readonly ZonedDateTime _searchStart = _searchEnd
.Minus(NodaTime.Duration.FromDays(365))
.InZone(tz);

private static readonly List<string> _manyCalendars = GetIcalStrings();

private static List<string> GetIcalStrings()
Expand All @@ -38,7 +44,7 @@ public List<Occurrence> SingleThreaded()
return _manyCalendars
.SelectMany(Calendar.Load<Calendar>)
.SelectMany(c => c.Events)
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Period.StartTime < _searchEnd))
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Start.ToInstant() < _searchEnd))
.ToList();
}

Expand All @@ -49,7 +55,7 @@ public List<Occurrence> ParallelUponDeserialize()
.AsParallel()
.SelectMany(Calendar.Load<Calendar>)
.SelectMany(c => c.Events)
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Period.StartTime < _searchEnd))
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Start.ToInstant() < _searchEnd))
.ToList();
}

Expand All @@ -60,7 +66,7 @@ public List<Occurrence> ParallelUponGetOccurrences()
.SelectMany(Calendar.Load<Calendar>)
.SelectMany(c => c.Events)
.AsParallel()
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Period.StartTime < _searchEnd))
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Start.ToInstant() < _searchEnd))
.ToList();
}

Expand All @@ -72,7 +78,7 @@ public List<Occurrence> ParallelDeserializeSequentialGatherEventsParallelGetOccu
.SelectMany(Calendar.Load<Calendar>)
.AsSequential()
.SelectMany(c => c.Events)
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Period.StartTime < _searchEnd))
.SelectMany(e => e.GetOccurrences(_searchStart).TakeWhile(p => p.Start.ToInstant() < _searchEnd))
.ToList();
}
}
113 changes: 92 additions & 21 deletions Ical.Net.Benchmarks/OccurencePerfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using BenchmarkDotNet.Attributes;
using Ical.Net.CalendarComponents;
using Ical.Net.DataTypes;
using NodaTime;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -17,6 +18,8 @@ public class OccurencePerfTests
private Calendar _calendarFourEvents = null!;
private Calendar _calendarWithRecurrences = null!;

private static DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];

[GlobalSetup]
public void Setup()
{
Expand All @@ -27,35 +30,69 @@ public void Setup()
[Benchmark]
public void GetOccurrences()
{
_ = _calendarWithRecurrences.GetOccurrences().ToList();
_ = _calendarWithRecurrences.GetOccurrences(tz).ToList();
}

[Benchmark]
public void MultipleEventsWithUntilOccurrencesSearchingByWholeCalendar()
{
var searchStart = _calendarFourEvents.Events.First().DtStart!.AddYears(-1);
var searchEnd = _calendarFourEvents.Events.Last().DtStart!.AddYears(1);
_ = _calendarFourEvents.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd);
var searchStart = _calendarFourEvents.Events.First().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(-1)
.InZoneLeniently(tz);

var searchEnd = _calendarFourEvents.Events.Last().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(1)
.InZoneLeniently(tz)
.ToInstant();

_ = _calendarFourEvents.GetOccurrences(searchStart).TakeWhile(p => p.Start.ToInstant() < searchEnd);
}

[Benchmark]
public void MultipleEventsWithUntilOccurrences()
{
var searchStart = _calendarFourEvents.Events.First().DtStart!.AddYears(-1);
var searchEnd = _calendarFourEvents.Events.Last().DtStart!.AddYears(1);
var searchStart = _calendarFourEvents.Events.First().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(-1)
.InZoneLeniently(tz);

var searchEnd = _calendarFourEvents.Events.Last().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(1)
.InZoneLeniently(tz)
.ToInstant();

_ = _calendarFourEvents.Events
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd))
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Start.ToInstant() < searchEnd))
.ToList();
}

[Benchmark]
public void MultipleEventsWithUntilOccurrencesEventsAsParallel()
{
var searchStart = _calendarFourEvents.Events.First().DtStart!.AddYears(-1);
var searchEnd = _calendarFourEvents.Events.Last().DtStart!.AddYears(1).AddDays(10);
var searchStart = _calendarFourEvents.Events.First().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(-1)
.InZoneLeniently(tz);

var searchEnd = _calendarFourEvents.Events.Last().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(1)
.PlusDays(10)
.InZoneLeniently(tz)
.ToInstant();

_ = _calendarFourEvents.Events
.AsParallel()
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd))
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Start.ToInstant() < searchEnd))
.ToList();
}

Expand Down Expand Up @@ -89,7 +126,7 @@ private static Calendar GetFourCalendarEventsWithUntilRule()
{
var rrule = new RecurrencePattern(FrequencyType.Daily, 1)
{
Until = new CalDateTime(startTime.AddDays(10)),
Until = startTime.AddDays(10),
};

var e = new CalendarEvent
Expand All @@ -98,7 +135,7 @@ private static Calendar GetFourCalendarEventsWithUntilRule()
End = startTime.AddMinutes(10).ToTimeZone(tzid),
RecurrenceRules = new List<RecurrencePattern> { rrule },
};
startTime = startTime.Add(Duration.FromTimeSpanExact(interval));
startTime = startTime.Add(DataTypes.Duration.FromTimeSpanExact(interval));
return e;
});

Expand All @@ -111,31 +148,65 @@ private static Calendar GetFourCalendarEventsWithUntilRule()
public void MultipleEventsWithCountOccurrencesSearchingByWholeCalendar()
{
var calendar = GetFourCalendarEventsWithCountRule();
var searchStart = calendar.Events.First().DtStart!.AddYears(-1);
var searchEnd = calendar.Events.Last().DtStart!.AddYears(1);
_ = calendar.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd);
var searchStart = _calendarFourEvents.Events.First().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(-1)
.InZoneLeniently(tz);

var searchEnd = _calendarFourEvents.Events.Last().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(1)
.InZoneLeniently(tz)
.ToInstant();

_ = calendar.GetOccurrences(searchStart).TakeWhile(p => p.Start.ToInstant() < searchEnd);
}

[Benchmark]
public void MultipleEventsWithCountOccurrences()
{
var calendar = GetFourCalendarEventsWithCountRule();
var searchStart = calendar.Events.First().DtStart!.AddYears(-1);
var searchEnd = calendar.Events.Last().DtStart!.AddYears(1);
var searchStart = _calendarFourEvents.Events.First().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(-1)
.InZoneLeniently(tz);

var searchEnd = _calendarFourEvents.Events.Last().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(1)
.InZoneLeniently(tz)
.ToInstant();

_ = calendar.Events
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd))
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Start.ToInstant() < searchEnd))
.ToList();
}

[Benchmark]
public void MultipleEventsWithCountOccurrencesEventsAsParallel()
{
var calendar = GetFourCalendarEventsWithCountRule();
var searchStart = calendar.Events.First().DtStart!.AddYears(-1);
var searchEnd = calendar.Events.Last().DtStart!.AddYears(1).AddDays(10);
var searchStart = _calendarFourEvents.Events.First().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(-1)
.InZoneLeniently(tz);

var searchEnd = _calendarFourEvents.Events.Last().DtStart!
.ToZonedDateTime(tz)
.LocalDateTime
.PlusYears(1)
.PlusDays(10)
.InZoneLeniently(tz)
.ToInstant();

_ = calendar.Events
.AsParallel()
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd))
.SelectMany(e => e.GetOccurrences(searchStart).TakeWhile(p => p.Start.ToInstant() < searchEnd))
.ToList();
}

Expand Down
21 changes: 15 additions & 6 deletions Ical.Net.Benchmarks/ThroughputTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
using BenchmarkDotNet.Attributes;
using System.Linq;
using Ical.Net.DataTypes;
using NodaTime;

namespace Ical.Net.Benchmarks;

public class ThroughputTests
{
private static DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];

[Benchmark]
public void DeserializeAndComputeUntilOccurrences()
{
Expand Down Expand Up @@ -68,9 +71,12 @@ rsion 08.00.0681.000"">\n<TITLE></TITLE>\n</HEAD>\n<BODY>\n<!-- Converted f

var calendar = Calendar.Load(e)!;
var calendarEvent = calendar.Events.First();
var searchStart = new CalDateTime(2009, 06, 20);
var searchEnd = new CalDateTime(2011, 06, 23);
var occurrences = calendarEvent.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd);
var searchStart = tz.AtStartOfDay(new(2009, 06, 20));
var searchEnd = tz.AtStartOfDay(new(2011, 06, 23)).ToInstant();
var _ = calendarEvent
.GetOccurrences(searchStart)
.TakeWhile(p => p.Start.ToInstant() < searchEnd)
.ToList();
}

[Benchmark]
Expand Down Expand Up @@ -130,8 +136,11 @@ rsion 08.00.0681.000"">\n<TITLE></TITLE>\n</HEAD>\n<BODY>\n<!-- Converted f

var calendar = Calendar.Load(e)!;
var calendarEvent = calendar.Events.First();
var searchStart = new CalDateTime(2009, 06, 20);
var searchEnd = new CalDateTime(2011, 06, 23);
var occurrences = calendarEvent.GetOccurrences(searchStart).TakeWhile(p => p.Period.StartTime < searchEnd);
var searchStart = tz.AtStartOfDay(new(2009, 06, 20));
var searchEnd = tz.AtStartOfDay(new(2011, 06, 23)).ToInstant();
var _ = calendarEvent
.GetOccurrences(searchStart)
.TakeWhile(p => p.Start.ToInstant() < searchEnd)
.ToList();
}
}
52 changes: 10 additions & 42 deletions Ical.Net.Tests/CalDateTimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ public void ToTimeZoneFloating()
[Test, TestCaseSource(nameof(ToTimeZoneTestCases))]
public void ToTimeZoneTests(CalendarEvent calendarEvent, string targetTimeZone)
{
var startAsUtc = calendarEvent.Start!.AsUtc;
var startAsUtc = calendarEvent.Start!.ToInstant();

var convertedStart = calendarEvent.Start.ToTimeZone(targetTimeZone);
var convertedAsUtc = convertedStart.AsUtc;
var convertedAsUtc = convertedStart.ToInstant();

Assert.That(convertedAsUtc, Is.EqualTo(startAsUtc));
}
Expand Down Expand Up @@ -97,11 +97,11 @@ public void SameDateTimeWithDifferentTzIdShouldReturnSameUtc()
var someTime = DateTimeOffset.Parse("2018-05-21T11:35:00-04:00", CultureInfo.InvariantCulture);

var someDt = new CalDateTime(someTime.DateTime, "America/New_York");
var firstUtc = someDt.AsUtc;
Assert.That(firstUtc, Is.EqualTo(someTime.UtcDateTime));
var firstUtc = someDt.ToInstant();
Assert.That(firstUtc, Is.EqualTo(NodaTime.Instant.FromDateTimeOffset(someTime)));

someDt = new CalDateTime(someTime.DateTime, "Europe/Berlin");
var berlinUtc = someDt.AsUtc;
var berlinUtc = someDt.ToInstant();
Assert.That(berlinUtc, Is.Not.EqualTo(firstUtc));
}

Expand Down Expand Up @@ -150,7 +150,7 @@ public string ToStringTests(CalDateTime calDateTime, string format, IFormatProvi
public static IEnumerable ToStringTestCases()
{
yield return new TestCaseData(new CalDateTime(2024, 8, 30, 10, 30, 0, tzId: "Pacific/Auckland"), "O", null)
.Returns("2024-08-30T10:30:00.0000000+12:00 Pacific/Auckland")
.Returns("2024-08-30T10:30:00.0000000 Pacific/Auckland")
.SetName("Date and time with 'O' format arg, default culture");

yield return new TestCaseData(new CalDateTime(2024, 8, 30), "O", null)
Expand All @@ -159,7 +159,7 @@ public static IEnumerable ToStringTestCases()

yield return new TestCaseData(new CalDateTime(2024, 8, 30, 10, 30, 0, tzId: "Pacific/Auckland"), "O",
CultureInfo.GetCultureInfo("fr-FR"))
.Returns("2024-08-30T10:30:00.0000000+12:00 Pacific/Auckland")
.Returns("2024-08-30T10:30:00.0000000 Pacific/Auckland")
.SetName("Date and time with 'O' format arg, French culture");

yield return new TestCaseData(new CalDateTime(2024, 8, 30, 10, 30, 0, tzId: "Pacific/Auckland"),
Expand Down Expand Up @@ -306,8 +306,8 @@ public void Simple_PropertyAndMethod_HasTime_Tests()
var c = new CalDateTime(dt, tzId: "Europe/Berlin");

var c2 = new CalDateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, c.TzId);
var c3 = new CalDateTime(new DateOnly(dt.Year, dt.Month, dt.Day),
new TimeOnly(dt.Hour, dt.Minute, dt.Second), c.TzId);
var c3 = new CalDateTime(new NodaTime.LocalDate(dt.Year, dt.Month, dt.Day),
new NodaTime.LocalTime(dt.Hour, dt.Minute, dt.Second), c.TzId);

Assert.Multiple(() =>
{
Expand All @@ -316,41 +316,9 @@ public void Simple_PropertyAndMethod_HasTime_Tests()
Assert.That(CalDateTime.UtcNow.Value.Kind, Is.EqualTo(DateTimeKind.Unspecified));
Assert.That(CalDateTime.Today.Value.Kind, Is.EqualTo(DateTimeKind.Unspecified));
Assert.That(c.DayOfYear, Is.EqualTo(dt.DayOfYear));
Assert.That(c.Time?.ToTimeSpan(), Is.EqualTo(dt.TimeOfDay));
Assert.That(c.Time, Is.EqualTo(NodaTime.LocalDateTime.FromDateTime(dt).TimeOfDay));
Assert.That(c.Add(-Duration.FromSeconds(dt.Second)).Value.Second, Is.EqualTo(0));
Assert.That(c.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture), Is.EqualTo("02.01.2025 Europe/Berlin"));
// Create a date-only CalDateTime from a CalDateTime
Assert.That(new CalDateTime(new CalDateTime(2025, 1, 1)), Is.EqualTo(new CalDateTime(2025, 1, 1)));
});
}

private static TestCaseData[] AddAndSubtractTestCases => [
new TestCaseData(new CalDateTime(2024, 10, 27, 0, 0, 0, tzId: null), 0),
new TestCaseData(new CalDateTime(2024, 10, 27, 0, 0, 0, tzId: CalDateTime.UtcTzId), 0),
new TestCaseData(new CalDateTime(2024, 10, 27, 0, 0, 0, tzId: "Europe/Paris"), 1)
];

[Test, TestCaseSource(nameof(AddAndSubtractTestCases))]
public void AddAndSubtract_ShouldBeReversible(CalDateTime t, int tzOffs)
{
var d = Duration.FromHours(4);
var expectedTimeSpan = d.ToTimeSpanUnspecified();

Assert.Multiple(() =>
{
Assert.That(t.Add(d).Add(-d), Is.EqualTo(t));
Assert.That(t.Add(d).SubtractExact(t), Is.EqualTo(expectedTimeSpan));
Assert.That(t.Add(d).SubtractExact(t), Is.EqualTo(d.ToTimeSpan(t)));
});

d = Duration.FromDays(1);
expectedTimeSpan = d.ToTimeSpanUnspecified().Add(TimeSpan.FromHours(tzOffs));

Assert.Multiple(() =>
{
Assert.That(t.Add(d).Add(-d), Is.EqualTo(t));
Assert.That(t.Add(d).SubtractExact(t), Is.EqualTo(expectedTimeSpan));
Assert.That(t.Add(d).SubtractExact(t), Is.EqualTo(d.ToTimeSpan(t)));
});
}

Expand Down
Loading
Loading