Update 2018-04-11: Most all of these changes have been published in ical.net versions 3 and 4. See the release notes for more details.
Downloading remote resources
When I ported ical.net to .NET Core, I removed the ability to download remote payloads from a URI. I did this for many reasons:
- There are myriad ways of accessing an HTTP resource. There are myriad ways of doing authentication. Consumers of ical.net are in a position to know the details of their environment, including security concerns, so responsibility for these concerns should lie with the developers using the library.
- Choosing to support
HttpClient
leaves .NET 4.0 users out in the cold. Choosing to supportWebClient
brings those people into the fold, but leaves .NET Core and WinRT users out. It also prevents developers working with newer versions of .NET from benefiting fromHttpClient
. - Non-blocking IO leaves developers working with WinForms and framework versions < 4.5 out in the cold. Bringing those developers back into the fold means we can’t make use of
async Task
s. Given the popularity of microservices and ical.net’s origins on the server side, this is a non-starter.
We can’t satisfy all use cases if we try to do everything, so instead I’ve decided that we’ll leave over-the-wire tasks to the developers using ical.net.
The primacy of strings
To that end… strings will be the primary way to work with ical.net. A developer should be able to instantiate everything from a huge collection of calendars down to a single calendar component (a VEVENT for example) by passing it a string that represents that thing. In modern C#, working directly with strings is more natural than passing Streams around, which is emblematic of old-school Java. It’s also more error prone: I fixed several memory leaks during the .NET Core port due to undisposed Streams)
- The constructor will be the deserializer. It is reasonable for the constructor to deserialize the textual representation into the typed representation.
ToString()
will be the serializer. It is reasonable forÂToString()
to serialize the typed representation into the textual representation.
Constructors as deserializers buys us…
Immutable types and (maybe) a fluid API
One of the challenges I faced when refactoring for performance was reasoning about mutable properties during serialization and deserialization. Today, deserialization makes extensive use of public, mutable properties. In fact, the documentation reflects this mutability:
var now = DateTime.Now; var later = now.AddHours(1); var rrule = new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 }; var e = new Event { DtStart = new CalDateTime(now), DtEnd = new CalDateTime(later), Duration = TimeSpan.FromHours(1), RecurrenceRules = new List<IRecurrencePattern> {rrule}, }; var calendar = new Calendar(); calendar.Events.Add(e);
To be completely honest, this state of affairs makes it quite difficult to make internal changes without breaking stuff. Many properties would naturally be getter-only, because they can be derived from simple internals, like Duration
above. Yet they’re explicitly set during deserialization. This is an incredible vector for bugs and breaking changes. (Ask me how I know…)
If we close these doors and windows, it will increase our internal maneuverability.
Fluid API
Look at the code above. Couldn’t it be more elegant? Shouldn’t it be? I don’t yet have a fully-formed idea of what a more fluid API might look like. Suggestions welcome.
Component names
IICalendarTypeNames
The .NET framework guidelines recommend prefixing interface names with “I”. The calendar spec is called “iCalendar”, as in “internet calendar”, which is an unfortunate coincidence. Naming conventions like IICalendarCollection
offend my sense of aesthetics, so I renamed some objects when I forked ical.net from dday. I’ve come around to valuing consistency over aesthetics, so I may go back to the double-I where it makes sense to do so.
CalDateTime
The object that represents “a DateTime with a time zone” is called a CalDateTime
. I’m not wild about this; we already have the .NET DateTime struct which has its own shortcomings that’ve been exhaustively documented elsewhere. A reasonable replacement for CalDateTime
might be a DateTimeOffset
with a string representation of an IANA, BCL, or Serialization time zone, with the time zone conversions delegated to NodaTime for computing recurrences. (In fact, NodaTime is already doing the heavy lifting behind the scenes for performance reasons, but the implementation isn’t pretty because of CalDateTime
‘s mutability. Were it immutable, it would have been a straightforward engine replacement.)
CalDateTime
is the lynchpin for most of the ical.net library. Most of its public properties should be simple expression bodies. Saner serialization and deserialization will have to come first as outlined above.
Divergence from spec completeness and adherence
VTIMEZONE
The iCalendar spec has ways of representing time change rules with VTIMEZONE. In the old days, dday.ical used this information to figure out Standard Time/Summer Time transitions. But as the spec itself notes:
Note: The specification of a global time zone registry is not addressed by this document and is left for future study. However, implementers may find the Olson time zone database [TZ] a useful reference. It is an informal, public-domain collection of time zone information, which is currently being maintained by volunteer Internet participants, and is used in several operating systems. This database contains current and historical time zone information for a wide variety of locations around the globe; it provides a time zone identifier for every unique time zone rule set in actual use since 1970, with historical data going back to the introduction of standard time.
At this point in time, the IANA (née Olson) tz database is the best source of truth. Relying on clients to specify reasonable time zone and time change behavior is unrealistic. I hope the spec authors revisit the VTIMEZONE
element, and instead have it specify a standard time zone string, preferably IANA.
To that end… ical.net will continue to preserve VTIMEZONE
fields, but it will not use them for recurrence computations or understanding Summer/Winter time changes. It will continue to rely on NodaTime for that.
URL and ATTACH
As mentioned above, ical.net will no longer include functionality to download resources from URIs. It will continue to preserve these fields so clients can do what they wish with the information they contain. This isn’t a divergence from the spec, per se, which doesn’t state that clients should provide facilities to download resources.