Reading and the model
Loading
Calendar loads from a path, a stream, a byte array or a string. Every loader takes optional
IcsParseOptions and a CancellationToken.
using XRay.Ics;
var fromFile = Calendar.LoadFile("team.ics");
var fromStream = Calendar.Load(File.OpenRead("team.ics"));
var fromBytes = Calendar.Load(bytes);
var fromText = Calendar.Parse("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nEND:VCALENDAR\r\n");
// Several VCALENDAR objects in one input are merged into the first by Load;
// LoadAll keeps them apart.
IReadOnlyList<Calendar> all = Calendar.LoadAll(File.OpenRead("feed.ics"));
The reader is tolerant:
- CRLF, LF and CR line ends can mix in one file; lines fold with a space or a tab.
- Quoted parameter values and RFC 6868 carets, TEXT unescaping,
X-properties and vCard-style groups are kept. - Encodings are detected: UTF-8 with or without a BOM, UTF-16 and UTF-32, and legacy bytes
through Windows-1252 or the code page a line names with
CHARSET=. - Damaged input is read as far as it goes (unclosed components, a stray
END, garbage lines, truncation). What was skipped is listed inCalendar.Warnings.
Only input that is not a calendar at all, or that exceeds a limit, raises IcsFormatException.
vCalendar 1.0 is read too: ENCODING=QUOTED-PRINTABLE with soft line breaks, CHARSET= on any
line, bare parameters, TZ, the DALARM, AALARM, PALARM and MALARM alarms, and the
D1 #5, W1 MO WE, MD1 1 15 style of recurrence rule. Calendar.IsVCalendar1 tells which
version was read.
The typed model
Calendar exposes Events, Todos, Journals, FreeBusies and TimeZones, plus the
calendar-level Version, ProductId, Method, Name and Description. Calendar.Primary is
the first event, else the first to-do, else the first journal entry.
Components derive from CalendarComponent:
| Member | Content |
|---|---|
Uid, Summary, Description, HtmlDescription |
Text. HtmlDescription is Outlook's X-ALT-DESC HTML body when there is one. |
Start, Created, LastModified, Stamp, RecurrenceId |
IcsDateTime values. |
Status, Classification, Sequence, Url, Comment, Categories |
Scalars and lists. |
Organizer, Attendees, RequiredAttendees, OptionalAttendees, NonParticipants, Chairs, Resources |
CalendarAddress values, split by role and user type. |
Attachments |
CalendarAttachment values. |
RecurrenceRules, RecurrenceDates, ExceptionDates, IsRecurring |
The recurrence set, see Recurrence. |
CalendarEvent adds Location, Geo, End, Duration, IsAllDay, Priority,
Transparency and Alarms.
The typed model is a view over the raw tree. Calendar.Raw and each component's Raw hold every
property and parameter as read, setters write to Raw, and getters parse on demand. A load
followed by a save therefore loses nothing, including properties the typed model has no name
for. Typed accessors never throw: a value that cannot be read is null. The wire form of a
value, with TEXT escapes in place, is IcsProperty.Value; use TextValue for the unescaped
text.
Times and time zones
IcsDateTime has a Kind: Date (all-day), Floating, Utc or Zoned.
Valueis the wall-clock reading as written.Offsetis the UTC offset when it can be known: UTC, and zones the calendar's ownVTIMEZONErules or the system can resolve.ToDateTimeOffset(floatingZone)reads floating times and dates in the zone you choose, UTC by default.ToLocalTime(zone)converts.- A zone nothing can resolve keeps its wall clock with
Offset == nullrather than a guess.
A TZID resolves through the calendar's VTIMEZONE rules first, then through IANA ids,
Windows zone names and Mozilla-style ids by way of TimeZoneInfo. Windows zone names come from a
built-in table of the common ones, not from CLDR data. X-WR-TIMEZONE is exposed as
Calendar.DefaultTimeZoneId but is not applied to floating times.
Attendees
CalendarAddress carries CommonName, Email (a mailto: prefix removed, and the EMAIL
parameter used when the value is not an address), Role, Status (participation status),
Rsvp, UserType, SentBy, Directory, and the delegation and membership lists.
Address is the e-mail address or the stripped URI, and DisplayName falls back to it when
there is no common name.
Attachments
CalendarAttachment covers inline base64 (FILENAME, X-FILENAME, FMTTYPE), data: URIs
and URI references.
using XRay.Ics;
var appointment = IcsExtractor.ExtractFile("meeting.ics");
foreach (var attachment in appointment.Attachments.Where(a => !string.IsNullOrWhiteSpace(a.Name)))
{
if (!attachment.HasData)
{
Console.WriteLine($"{attachment.Name}: reference to {attachment.Uri}"); // IsInline is false
continue;
}
await using var output = File.Create(Path.Combine(folder, attachment.Name!));
attachment.WriteTo(output, token); // decodes block by block, no second copy
}
GetBytes() returns the decoded content and Open() a read-only stream over it. Size is
null for a reference, and DeclaredSize is the SIZE parameter when the file states one.
A damaged base64 payload decodes as far as it is valid.