コード例 #1
0
        /// <summary>
        /// Open a vCalendar or iCalendar file
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event arguments</param>
        private void miOpen_Click(object sender, EventArgs e)
        {
            if (wasModified && MessageBox.Show("Do you want to discard your changes to the current calendar?",
                                               "Discard Changes", MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                                               MessageBoxDefaultButton.Button2) == DialogResult.No)
            {
                return;
            }

            using (OpenFileDialog dlg = new OpenFileDialog())
            {
                dlg.Title  = "Load Calendar File";
                dlg.Filter = "ICS files (*.ics)|*.ics|VCS files (*.vcs)|*.vcs|All files (*.*)|*.*";

                if (vCal.Version == SpecificationVersions.vCalendar10)
                {
                    dlg.DefaultExt  = "vcs";
                    dlg.FilterIndex = 2;
                }
                else
                {
                    dlg.DefaultExt  = "ics";
                    dlg.FilterIndex = 1;
                }

                dlg.InitialDirectory = Path.GetFullPath(Path.Combine(
                                                            Environment.CurrentDirectory, @"..\..\..\..\..\PDIFiles"));

                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        this.Cursor = Cursors.WaitCursor;

                        // Parse the calendar information from the file and load the data grid with some basic
                        // information about the items in it.
                        vCal.Dispose();
                        vCal = VCalendarParser.ParseFromFile(dlg.FileName);

                        LoadComponentList();

                        // Find the first collection with items
                        if (vCal.Events.Count != 0)
                        {
                            cboComponents.SelectedIndex = 0;
                        }
                        else
                        if (vCal.ToDos.Count != 0)
                        {
                            cboComponents.SelectedIndex = 1;
                        }
                        else
                        if (vCal.Journals.Count != 0)
                        {
                            cboComponents.SelectedIndex = 2;
                        }
                        else
                        if (vCal.FreeBusys.Count != 0)
                        {
                            cboComponents.SelectedIndex = 3;
                        }
                        else
                        {
                            cboComponents.SelectedIndex = 0;
                        }

                        LoadGridWithItems(true);
                        lblFilename.Text = dlg.FileName;
                    }
                    catch (Exception ex)
                    {
                        string error = String.Format("Unable to load calendar:\n{0}", ex.Message);

                        if (ex.InnerException != null)
                        {
                            error += ex.InnerException.Message + "\n";

                            if (ex.InnerException.InnerException != null)
                            {
                                error += ex.InnerException.InnerException.Message;
                            }
                        }

                        System.Diagnostics.Debug.Write(ex);

                        MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    finally
                    {
                        this.Cursor = Cursors.Default;
                    }
                }
            }
        }
コード例 #2
0
        public void ToStringTest()
        {
            var       calString    = @"BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:[email protected]
X-WR-TIMEZONE:America/Los_Angeles
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
RDATE:19450603T010000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=America/Los_Angeles:20120629T130000
DTEND;TZID=America/Los_Angeles:20120629T140000
DTSTAMP:20120629T112428Z
UID:[email protected]
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
CREATED:20120629T111935Z
DESCRIPTION:foo
LAST-MODIFIED:20120629T112428Z
LOCATION:Barcelona
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Demo B2G Calendar
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:EMAIL
DESCRIPTION:This is an event reminder
SUMMARY:Alarm notification
ATTENDEE:mailto:[email protected]
TRIGGER:-P0DT0H30M0S
END:VALARM
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:This is an event reminder
TRIGGER:-P0DT0H30M0S
END:VALARM
END:VEVENT
END:VCALENDAR
";
            VCalendar calendar     = VCalendar.Parse(calString);
            var       xmlDoc       = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<C:calendar-query xmlns:D=""DAV:""
xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<D:prop>
<D:getetag/>
<C:calendar-data>
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name=""VCALENDAR"">
<C:comp-filter name=""VEVENT"">
	<C:time-range start=""20060104T000000Z""
end=""20060105T000000Z""/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>";
            var       xmlTree      = XmlTreeStructure.Parse(xmlDoc);
            var       node         = xmlTree.GetChildAtAnyLevel("calendar-data");
            var       newCalString = calendar.ToString(node);
            var       newCal       = new VCalendar(newCalString);

            Assert.Equal(2, newCal.CalendarComponents.Count);
            Assert.Contains("VEVENT", newCal.CalendarComponents.Keys);
            Assert.Contains("VTIMEZONE", newCal.CalendarComponents.Keys);
            Assert.Equal(4, newCal.CalendarComponents["VEVENT"].First().Properties.Count);
        }
コード例 #3
0
        public void BuildVCalendar()
        {
            var             calString      = @"BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:[email protected]
X-WR-TIMEZONE:America/Los_Angeles
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=America/Los_Angeles:20120629T130000
DTEND;TZID=America/Los_Angeles:20120629T140000
DTSTAMP:20120629T112428Z
UID:[email protected]
RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;
 BYMONTHDAY=2,3,4,5,6,7,8
RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;
 BYMONTHDAY=2,3,4,5,6,7,8
CREATED:20120629T111935Z
DESCRIPTION:foo
LAST-MODIFIED:20120629T112428Z
LOCATION:Barcelona
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Demo B2G Calendar
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:EMAIL
DESCRIPTION:This is an event reminder
SUMMARY:Alarm notification
ATTENDEE:mailto:[email protected]
ATTENDEE:mailto:[email protected]
TRIGGER:-P0DT0H30M0S
END:VALARM
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:This is an event reminder
TRIGGER:-P0DT0H30M0S
END:VALARM
END:VEVENT
END:VCALENDAR
";
            VCalendar       calendar       = VCalendar.Parse(calString);
            var             calendarString = calendar.ToString();
            UnicodeEncoding uniencoding    = new UnicodeEncoding();
            UTF8Encoding    utf8Encoding   = new UTF8Encoding();
            var             toWrite        = utf8Encoding.GetBytes(calendarString);

            File.Delete("output1.ics");
            using (var writer = File.OpenWrite("output1.ics"))
            {
                writer.Seek(0, SeekOrigin.End);
                writer.Write(toWrite, 0, toWrite.Length);
            }
            using (var reader = File.OpenText("output1.ics"))
            {
                var writedCal      = reader.ReadToEnd();
                var writedCalLines = Parser.CalendarReader(writedCal);
                var expectedLines  = Parser.CalendarReader(calString);
                Assert.Equal(expectedLines.Length, writedCalLines.Length);
                for (int i = 0; i < writedCalLines.Length; i++)
                {
                    Assert.Contains(expectedLines[i], writedCalLines);
                }
            }
            Assert.NotNull(calendarString);
        }
コード例 #4
0
        /// <summary>
        ///     The CALDAV:calendar-multiget REPORT is used to retrieve specific calendar object resources from within a
        ///     collection, if the Request-URI is a collection, or to retrieve a specific calendar object resource, if the
        ///     Request-URI is a calendar object resource. This report is similar to the CALDAV:calendar-query REPORT
        ///     (see Section 7.8), except that it takes a list of DAV:href elements, instead of a CALDAV:filter element, to
        ///     determine which calendar object resources to return
        /// </summary>
        /// <returns></returns>
        private async Task CalendarMultiget(IXMLTreeStructure xmlBody, HttpContext httpContext)
        {
            // take the first prop node to know the data that
            // should ne returned
            IXMLTreeStructure propNode;

            xmlBody.GetChildAtAnyLevel("prop", out propNode);

            //take the href nodes. Contain the direction of the resources files that
            //are requested
            var hrefs = xmlBody.Children.Where(node => node.NodeName == "href").Select(href => href.Value);

            var result = new Dictionary <string, string>();

            // process the requested resources
            foreach (var href in hrefs)
            {
                var fs = new FileSystemManagement();

                var resourceContent = await fs.GetCalendarObjectResource(href);

                result.Add(href, resourceContent);
            }
            await ReportResponseBuilder(result
                                        .Select(
                                            x =>
                                            new KeyValuePair <string, VCalendar>(x.Key,
                                                                                 string.IsNullOrEmpty(x.Value) ? null : VCalendar.Parse(x.Value))), propNode, httpContext);
        }
コード例 #5
0
        public void CheckProperties()
        {
            var       calString   = @"BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:[email protected]
X-WR-TIMEZONE:America/Los_Angeles
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=America/Los_Angeles:20120629T130000
DTEND;TZID=America/Los_Angeles:20120629T140000
DTSTAMP:20120629T112428Z
UID:[email protected]
CREATED:20120629T111935Z
DESCRIPTION:foo
LAST-MODIFIED:20120629T112428Z
LOCATION:Barcelona
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Demo B2G Calendar
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:EMAIL
DESCRIPTION:This is an event reminder
SUMMARY:Alarm notification
ATTENDEE:mailto:[email protected]
TRIGGER:-P0DT0H30M0S
END:VALARM
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:This is an event reminder
TRIGGER:-P0DT0H30M0S
END:VALARM
END:VEVENT
END:VCALENDAR
";
            VCalendar calendar    = VCalendar.Parse(calString);
            var       calEvent    = calendar.GetCalendarComponents("VEVENT").First() as VEvent;
            var       eventAlarms = calEvent.CalendarComponents["VALARM"];

            Assert.Equal((calEvent.GetComponentProperty("UID") as IValue <string>).Value, "*****@*****.**");
            Assert.Equal((calEvent.GetComponentProperty("DESCRIPTION") as IValue <string>).Value, "foo");
            Assert.Equal(2, eventAlarms.Count);
        }
コード例 #6
0
        public async Task <bool> PreconditionsOK(Dictionary <string, string> propertiesAndHeaders, HttpResponse response)
        {
            #region Extracting Properties

            var url = propertiesAndHeaders["url"];

            var       contentSize = propertiesAndHeaders["content-length"];
            var       body        = propertiesAndHeaders["body"];
            VCalendar iCalendar;
            try
            {
                iCalendar = new VCalendar(body); //lo que no estoy seguro que en el body solo haya el iCal string
            }
            catch (Exception)
            {
                response.StatusCode = (int)HttpStatusCode.BadRequest;
                return(false);
            }

            #endregion

            //check that resourceId don't exist but the collection does.
            if (
                !StorageManagement.ExistCalendarCollection(url.Remove(url.LastIndexOf("/", StringComparison.Ordinal) + 1)))
            {
                response.StatusCode = (int)HttpStatusCode.NotFound;
                return(false);
            }


            //check that if the resource exist then all its components different of VTIMEZONE has to have the same UID
            //if the resource not exist can not be another resource with the same uid.
            if (!StorageManagement.ExistCalendarObjectResource(url))
            {
                var component = iCalendar.CalendarComponents.FirstOrDefault(comp => comp.Key != "VTIMEZONE").Value;
                var uid       = component.FirstOrDefault()?.Properties["UID"].StringValue;
                // var resource = db.GetCalendarResource(userEmail, collectionName, calendarResourceId);
                var collection =
                    _collectionRepository.Get(url.Remove(url.LastIndexOf("/", StringComparison.Ordinal) + 1));
                foreach (var calendarresource in collection.CalendarResources)
                {
                    if (uid == calendarresource.Uid)
                    {
                        response.StatusCode = (int)HttpStatusCode.Conflict;
                        response.Body.Write(
                            $@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<no-uid-conflict xmlns='urn:ietf:params:xml:ns:caldav'>
<href xmlns='DAV:'>{
                                SystemProperties._baseUrl + calendarresource
                                    .Href}</href>
</no-uid-conflict>
</error>");
                        return(false);
                    }
                }
            }
            else
            {
                //If the resource exist the procedure is update and for that the uid has to be the same.
                var components        = iCalendar.CalendarComponents.FirstOrDefault(comp => comp.Key != "VTIMEZONE").Value;
                var calendarComponent = components.FirstOrDefault();
                if (calendarComponent != null)
                {
                    var uid = calendarComponent.Properties["UID"].StringValue;

                    var resource = _resourceRespository.Get(url);

                    if (resource.Uid != null && resource.Uid != uid)
                    {
                        response.StatusCode = (int)HttpStatusCode.Conflict;
                        response.Body.Write(
                            $@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<no-uid-conflict xmlns='urn:ietf:params:xml:ns:caldav'>
<href xmlns='DAV:'>{
                                SystemProperties._baseUrl + resource
                                    .Href}</href>
</no-uid-conflict>
</error>");
                        return(false);
                    }
                }
            }


            if (propertiesAndHeaders.ContainsKey("If-Match"))
            {
                //check that the value do exist
                if (!StorageManagement.ExistCalendarObjectResource(url))
                {
                    response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                    return(false);
                }
            }

            if (propertiesAndHeaders.ContainsKey("If-None-Match"))
            {
                //check that the value do not exist
                if (StorageManagement.ExistCalendarObjectResource(url))
                {
                    response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                    return(false);
                }
            }


            //it does not contain more than two calendar components
            //and if it has 2, one must be VTIMEZONE
            if (iCalendar.CalendarComponents.Count > 2)
            {
                if (!iCalendar.CalendarComponents.ContainsKey("VTIMEZONE"))
                {
                    response.StatusCode = (int)HttpStatusCode.Conflict;
                    response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
VTimezone Calendar Component Must be present.
</error-description>
</error>");
                    return(false);
                }

                var calendarComponents =
                    iCalendar.CalendarComponents.FirstOrDefault(comp => comp.Key != "VTIMEZONE").Value;

                //A Calendar Component can be separated in multiples calendar components but all MUST
                //have the same UID.
                var calendarComponent = calendarComponents.FirstOrDefault();
                if (calendarComponent != null)
                {
                    var uid = calendarComponent.Properties["UID"].StringValue;
                    foreach (var component in calendarComponents)
                    {
                        var uidComp = component.Properties["UID"].StringValue;
                        if (uid != uidComp)
                        {
                            response.StatusCode = (int)HttpStatusCode.Conflict;
                            response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
If the count of calendar components execeds 2 including VTimezone the rest must have the same Uid and the same type.
</error-description>
</error>");
                            return(false);
                        }
                    }
                }

                //                response.StatusCode = (int)HttpStatusCode.Conflict;
                //                response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
                //<error xmlns='DAV:'>
                //<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
                //<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
                //Wrong amount of calendar components
                //</error-description>
                //</error>");
                //                return false;
            }

            //precondition responsible of check that an VTIMEZONE is obligatory
            if (iCalendar.CalendarComponents.Count == 2)
            {
                if (!iCalendar.CalendarComponents.ContainsKey("VTIMEZONE"))
                {
                    response.StatusCode = (int)HttpStatusCode.Conflict;
                    response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
VTimezone Calendar Component Must be present.
</error-description>
</error>");
                    return(false);
                }
            }


            //var uidCalendar = ((ComponentProperty<string>)iCalendar.Properties["UID"]).Value;
            ////Check that if the operation is create there is not another element in the collection with the same UID
            //if (!StorageManagement.ExistCalendarObjectResource(calendarResourceId))
            //{
            //    using (db)
            //    {
            //        if ((from calendarResource in db.CalendarResources
            //             where calendarResource.Uid == uidCalendar
            //             select calendarResource).Count() > 0)
            //            return false;

            //    }
            //}
            ////Check if the operation is update the element to be updated must have the same UID.
            //else
            //{
            //    using (db)
            //    {
            //        if ((from calendarResource in db.CalendarResources
            //             where calendarResource.Uid == uidCalendar
            //             select calendarResource).Count() == 0)
            //            return false;

            //    }
            //}

            var methodProp = iCalendar.GetComponentProperties("METHOD");
            //iCalendar object MUST NOT implement METHOD property
            if (!string.IsNullOrEmpty(methodProp?.StringValue))
            {
                response.StatusCode = (int)HttpStatusCode.Conflict;
                response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
Method prop must not be present
</error-description>
</error>");
                return(false);
            }

            //This precondition is the one in charge of check that the size of the body of the resource
            //included in the request dont exceeds the max-resource-size property of the colletion.
            int contentSizeInt;
            //for that i need that the controller has as request header content-size available
            if (!string.IsNullOrEmpty(contentSize) && int.TryParse(contentSize, out contentSizeInt))
            {
                var collection =
                    _collectionRepository.Get(url.Remove(url.LastIndexOf("/", StringComparison.Ordinal) + 1));
                //here the max-resource-property of the collection is called.
                var maxSize =
                    collection.Properties.FirstOrDefault(
                        p => p.Name == "max-resource-size" && p.Namespace == "urn:ietf:params:xml:ns:caldav");
                int maxSizeInt;
                if (int.TryParse(XmlTreeStructure.Parse(maxSize?.Value).Value, out maxSizeInt) &&
                    contentSizeInt > maxSizeInt)
                {
                    response.StatusCode = (int)HttpStatusCode.Conflict;
                    response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<max-resource-size xmlns='urn:ietf:params:xml:ns:caldav'></max-resource-size>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
Content size exceeds max size allowed.
</error-description>
</error>");
                    return(false);
                }
            }


            //TODO: Checking that all DateTime values are less-equal than
            //the max-date-time

            //TODO: Checking that all DateTime values are grater-equal than
            //the min-date-time

            //TODO: Checking that the number of recurring instances is less-equal
            //than the max-instances property value.

            return(await Task.FromResult(true));
        }
コード例 #7
0
 public static bool CalendarWriter(TextWriter writer, VCalendar calendar)
 {
     return(false);
 }
コード例 #8
0
ファイル: RecurringObject.cs プロジェクト: ywscr/PDI
        /// <summary>
        /// This method is used to return all recurring instances between the two specified date/times based on
        /// the current settings.
        /// </summary>
        /// <param name="fromDate">The minimum date/time on or after which instances should occur.  This will
        /// include an instance if it starts before the date/time but overlaps it when its duration is added to
        /// its start time.</param>
        /// <param name="toDate">The maximum date/time on or before which instances should occur.  This will
        /// include an instance if it starts on or before the specified date/time regardless of its duration.</param>
        /// <param name="inLocalTime">If true, the date/time parameters are assumed to be in local time and the
        /// returned date/times are expressed in local time.  If false, the date/time parameters are assumed to
        /// be in the time zone of the object and the returned date/times are expressed in the time zone of the
        /// object as specified by the <see cref="TimeZoneId"/> property.  If no time zone ID has been specified
        /// or it cannot be found, local time is used.</param>
        /// <returns>Returns a <see cref="DateTimeInstanceCollection"/> containing <see cref="DateTimeInstance" />
        /// objects that represent all instances found between the two specified date/times.  Instances may have
        /// a different duration if created from an <c>RDATE</c> property.</returns>
        /// <exception cref="ArgumentException">This is thrown if a start date has not been specified (it equals
        /// <c>DateTime.MinValue</c>) or the duration is negative.</exception>
        /// <seealso cref="AllInstances"/>
        /// <seealso cref="OccursOn"/>
        public DateTimeInstanceCollection InstancesBetween(DateTime fromDate, DateTime toDate, bool inLocalTime)
        {
            DateTimeCollection recurDates;
            Period             p;
            DateTime           endDate, tempDate1 = DateTime.MaxValue, tempDate2;
            int    idx, count;
            string timeZoneID = this.TimeZoneId;

            PeriodCollection periods   = new PeriodCollection();
            DateTime         startDate = this.StartDateTime.TimeZoneDateTime;
            Duration         dur       = this.InstanceDuration;

            if (startDate == DateTime.MinValue)
            {
                throw new ArgumentException(LR.GetString("ExNoComponentStartDate"));
            }

            if (dur.Ticks < 0)
            {
                throw new ArgumentException(LR.GetString("ExRONegativeDuration"));
            }

            // Convert fromDate and toDate to time zone time if inLocalTime is true.  Recurrences are always
            // resolved in the time of the object.
            if (inLocalTime && timeZoneID != null)
            {
                fromDate = VCalendar.LocalTimeToTimeZoneTime(fromDate, timeZoneID).StartDateTime;
                toDate   = VCalendar.LocalTimeToTimeZoneTime(toDate, timeZoneID).StartDateTime;
            }

            // There might be instances that overlap the requested range so we'll adjust the From date/time by
            // the duration to catch them.
            if (dur.Ticks > 1)
            {
                fromDate = fromDate.Add(new TimeSpan(0 - dur.Ticks + 1));
            }

            // As per the spec, the start date/time is always included in the set but only if it (or it's
            // duration) is within the requested range.  However, if it is recurring and the custom Exclude Start
            // property is set to true, it is not added.
            p = new Period(startDate, dur);

            if (((p.StartDateTime >= fromDate && p.StartDateTime <= toDate) || (p.EndDateTime >= fromDate &&
                                                                                p.EndDateTime <= toDate)) && (!this.IsRecurring || !this.ExcludeStartDateTime))
            {
                periods.Add(p);
            }

            // If it isn't recurring or starts after the requested end date, just return the collection as it is
            if (this.IsRecurring && startDate <= toDate)
            {
                // Expand each recurrence rule
                foreach (RRuleProperty rr in this.RecurrenceRules)
                {
                    // If used, RecurUntil is stored in Universal Time which is converted to local time.  If a
                    // time zone ID is specified, convert it to that time zone temporarily to make sure things
                    // are calculated correctly.
                    if (rr.Recurrence.MaximumOccurrences == 0 && timeZoneID != null)
                    {
                        tempDate1 = rr.Recurrence.RecurUntil;
                        rr.Recurrence.RecurUntil = VCalendar.LocalTimeToTimeZoneTime(tempDate1, timeZoneID).StartDateTime;
                    }

                    rr.Recurrence.StartDateTime = startDate;
                    recurDates = rr.Recurrence.InstancesBetween(fromDate, toDate);

                    if (rr.Recurrence.MaximumOccurrences == 0 && timeZoneID != null)
                    {
                        rr.Recurrence.RecurUntil = tempDate1;
                    }

                    foreach (DateTime dt in recurDates)
                    {
                        periods.Add(new Period(dt, dur));
                    }
                }

                // Add on recurrence dates within the range
                foreach (RDateProperty rd in this.RecurDates)
                {
                    if (rd.ValueLocation != ValLocValue.Period)
                    {
                        // If it's not a period, use the component's duration.  If it's only a date, assume it's
                        // the whole day.  The spec is not clear on this so I'm making a best guess.
                        if (rd.ValueLocation == ValLocValue.DateTime)
                        {
                            endDate = rd.TimeZoneDateTime.Add(dur.TimeSpan);
                        }
                        else
                        {
                            endDate = rd.TimeZoneDateTime.Add(new TimeSpan(TimeSpan.TicksPerDay));
                        }

                        if ((rd.TimeZoneDateTime >= fromDate && rd.TimeZoneDateTime <= toDate) ||
                            (endDate >= fromDate && endDate <= toDate))
                        {
                            periods.Add(new Period(rd.TimeZoneDateTime, endDate));
                        }
                    }
                    else
                    {
                        // As with Recurrence.RecurUntil, the period values are in Universal Time so convert them
                        // to the time zone time for proper comparison.
                        tempDate1 = rd.PeriodValue.StartDateTime;
                        tempDate2 = rd.PeriodValue.EndDateTime;

                        if (timeZoneID != null)
                        {
                            tempDate1 = VCalendar.LocalTimeToTimeZoneTime(tempDate1, timeZoneID).StartDateTime;
                        }

                        if (timeZoneID != null)
                        {
                            tempDate2 = VCalendar.LocalTimeToTimeZoneTime(tempDate2, timeZoneID).StartDateTime;
                        }

                        if ((tempDate1 >= fromDate && tempDate1 <= toDate) ||
                            (tempDate2 >= fromDate && tempDate2 <= toDate))
                        {
                            periods.Add(new Period(tempDate1, tempDate2));
                        }
                    }
                }

                // Expand exception rules and filter out those instances
                count = periods.Count;

                foreach (RRuleProperty er in this.ExceptionRules)
                {
                    // Same as above
                    if (er.Recurrence.MaximumOccurrences == 0 && timeZoneID != null)
                    {
                        tempDate1 = er.Recurrence.RecurUntil;
                        er.Recurrence.RecurUntil = VCalendar.LocalTimeToTimeZoneTime(tempDate1, timeZoneID).StartDateTime;
                    }

                    er.Recurrence.StartDateTime = startDate;
                    recurDates = er.Recurrence.InstancesBetween(fromDate, toDate);

                    if (er.Recurrence.MaximumOccurrences == 0 && timeZoneID != null)
                    {
                        er.Recurrence.RecurUntil = tempDate1;
                    }

                    foreach (DateTime dt in recurDates)
                    {
                        for (idx = 0; idx < count; idx++)
                        {
                            if (periods[idx].StartDateTime == dt)
                            {
                                periods.RemoveAt(idx);
                                idx--;
                                count--;
                            }
                        }
                    }
                }

                // Filter out any exception dates
                foreach (ExDateProperty ed in this.ExceptionDates)
                {
                    DateTime dt = ed.TimeZoneDateTime;

                    // If it's only a date, assume it's the whole day and remove all instances on that day
                    // regardless of the time.  The spec is not clear on this so I'm making a best guess.
                    if (ed.ValueLocation == ValLocValue.DateTime)
                    {
                        if (dt >= fromDate && dt <= toDate)
                        {
                            for (idx = 0; idx < count; idx++)
                            {
                                if (periods[idx].StartDateTime == dt)
                                {
                                    periods.RemoveAt(idx);
                                    idx--;
                                    count--;
                                }
                            }
                        }
                    }
                    else
                    if (dt >= fromDate.Date && dt <= toDate.Date)
                    {
                        for (idx = 0; idx < count; idx++)
                        {
                            if (periods[idx].StartDateTime.Date == dt)
                            {
                                periods.RemoveAt(idx);
                                idx--;
                                count--;
                            }
                        }
                    }
                }

                // Sort the periods and remove duplicates
                periods.Sort(true);

                for (idx = 1; idx < count; idx++)
                {
                    if (periods[idx] == periods[idx - 1])
                    {
                        periods.RemoveAt(idx);
                        idx--;
                        count--;
                    }
                }
            }

            // Now convert the periods to DateTimeInstances that include the necessary time zone information
            DateTimeInstanceCollection dtic = new DateTimeInstanceCollection();
            DateTimeInstance           dti, dtiEnd;

            // Always in local time if there is no time zone ID
            if (timeZoneID == null)
            {
                inLocalTime = true;
            }

            foreach (Period pd in periods)
            {
                if (inLocalTime)
                {
                    dti    = VCalendar.TimeZoneTimeToLocalTime(pd.StartDateTime, timeZoneID);
                    dtiEnd = VCalendar.TimeZoneTimeToLocalTime(pd.EndDateTime, timeZoneID);
                }
                else
                {
                    dti    = VCalendar.TimeZoneTimeInfo(pd.StartDateTime, timeZoneID);
                    dtiEnd = VCalendar.TimeZoneTimeInfo(pd.EndDateTime, timeZoneID);
                }

                dti.Duration                = pd.Duration;
                dti.EndDateTime             = dtiEnd.EndDateTime;
                dti.EndIsDaylightSavingTime = dtiEnd.EndIsDaylightSavingTime;
                dti.EndTimeZoneName         = dtiEnd.EndTimeZoneName;

                // If it already contains the entry and it is in DST, bump it forward an hour to account for the
                // time adjustment.  This will happen on hourly, minutely, and secondly recurrence patterns.  By
                // moving duplicates forward an hour, we retain the expected number of occurrences.
                if (!dtic.Contains(dti))
                {
                    dtic.Add(dti);
                }
                else
                if (dti.StartIsDaylightSavingTime)
                {
                    dti.StartDateTime = dti.StartDateTime.AddHours(1);
                    dti.EndDateTime   = dti.EndDateTime.AddHours(1);
                    dtic.Add(dti);
                }
            }

            return(dtic);     // And finally, we are done
        }
コード例 #9
0
        /// <summary>
        /// Returns a list of the user's calendar items.
        /// </summary>
        /// <param name="UserName">User name used for logging in</param>
        /// <param name="Password">Password for logging in</param>
        /// <param name="Directory">User's directory</param>
        /// <param name="StartDate">Date to start at</param>
        /// <param name="EndDate">Date to end at</param>
        /// <param name="Server">Server Name</param>
        /// <returns>A list of the user's calendar items in VCalendar format, between two date ranges</returns>
        public static List <VCalendar> GetCalendarItems(string UserName, string Password, string Server, string Directory, DateTime StartDate, DateTime EndDate)
        {
            List <VCalendar> ReturnArray = new List <VCalendar>();
            string           Uri         = string.Format("http://{0}/exchange/{1}/calendar/", Server, Directory);

            string Query = "<?xml version=\"1.0\"?>"
                           + "<g:searchrequest xmlns:g=\"DAV:\">"
                           + "<g:sql>SELECT \"urn:schemas:calendar:location\", \"urn:schemas:httpmail:subject\", "
                           + "\"urn:schemas:httpmail:textdescription\", "
                           + "\"urn:schemas:calendar:dtstart\", \"urn:schemas:calendar:dtend\", "
                           + "\"urn:schemas:calendar:busystatus\", \"urn:schemas:calendar:instancetype\" "
                           + "FROM Scope('SHALLOW TRAVERSAL OF \"" + Uri + "\"') "
                           + "WHERE ((\"urn:schemas:calendar:dtstart\" &gt;= '" + StartDate.ToString("yyyy/MM/dd hh:mm:ss") + "' "
                           + "AND \"urn:schemas:calendar:dtstart\" &lt;= '" + EndDate.ToString("yyyy/MM/dd hh:mm:ss") + "') "
                           + "OR (\"urn:schemas:calendar:dtend\" &gt;= '" + StartDate.ToString("yyyy/MM/dd hh:mm:ss") + "' "
                           + "AND \"urn:schemas:calendar:dtstart\" &lt;= '" + EndDate.ToString("yyyy/MM/dd hh:mm:ss") + "')) "
                           + "AND \"DAV:contentclass\" = 'urn:content-classes:appointment' "
                           + "AND NOT \"urn:schemas:calendar:instancetype\" = 1 "
                           + "ORDER BY \"urn:schemas:calendar:dtstart\" ASC"
                           + "</g:sql></g:searchrequest>";

            byte[] Contents = System.Text.Encoding.UTF8.GetBytes(Query);

            HttpWebRequest Request = HttpWebRequest.Create(Uri) as HttpWebRequest;

            System.Net.CredentialCache MyCredentialCache = new System.Net.CredentialCache();
            if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
            {
                MyCredentialCache.Add(new System.Uri(Uri),
                                      "NTLM",
                                      new System.Net.NetworkCredential(UserName, Password));
            }
            else
            {
                MyCredentialCache.Add(new System.Uri(Uri),
                                      "Negotiate",
                                      (System.Net.NetworkCredential)CredentialCache.DefaultCredentials);
            }
            Request.Credentials   = MyCredentialCache;
            Request.Method        = "SEARCH";
            Request.ContentLength = Contents.Length;
            Request.ContentType   = "text/xml";

            using (System.IO.Stream RequestStream = Request.GetRequestStream())
            {
                RequestStream.Write(Contents, 0, Contents.Length);

                using (HttpWebResponse Response = Request.GetResponse() as HttpWebResponse)
                {
                    using (System.IO.Stream ResponseStream = Response.GetResponseStream())
                    {
                        XmlDocument Document = new XmlDocument();
                        Document.Load(ResponseStream);
                        foreach (XmlElement Element in Document.GetElementsByTagName("a:prop"))
                        {
                            VCalendar Cal = new VCalendar();
                            if (Element["e:textdescription"] != null)
                            {
                                Cal.Description = Element["e:textdescription"].InnerText;
                            }
                            if (Element["e:subject"] != null)
                            {
                                Cal.Subject = Element["e:subject"].InnerText;
                            }
                            if (Element["d:location"] != null)
                            {
                                Cal.Location = Element["d:location"].InnerText;
                            }
                            if (Element["d:dtstart"] != null)
                            {
                                Cal.StartTime = DateTime.Parse(Element["d:dtstart"].InnerText);
                            }
                            if (Element["d:dtend"] != null)
                            {
                                Cal.EndTime = DateTime.Parse(Element["d:dtend"].InnerText);
                            }
                            ReturnArray.Add(Cal);
                        }
                    }
                }
            }
            return(ReturnArray);
        }
コード例 #10
0
        public void TestTheToStringMethod()
        {
            var vCalString = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:+//IDN bitfire.at//DAVdroid/1.0.8 ical4android ical4j/2.x
BEGIN:VEVENT
DTSTAMP:20160907T213117Z
UID:1d2a9f2f-40d0-4733-b81b-1ea15d79ee21
DTSTART;TZID=America/Havana:20160905T080000
DURATION:PT5700S
RRULE:FREQ=WEEKLY;WKST=SU;COUNT=16;BYDAY=MO
SUMMARY:Programación y algoritmos CF
LOCATION:Aula 8
STATUS:CONFIRMED
END:VEVENT
BEGIN:VTIMEZONE
TZID:America/Havana
TZURL:http://tzurl.org/zoneinfo/America/Havana
X-LIC-LOCATION:America/Havana
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:CDT
DTSTART:19280610T010000
RDATE:19280610T010000
RDATE:19400602T010000
RDATE:19410601T010000
RDATE:19420607T010000
RDATE:19450603T010000
RDATE:19460602T010000
RDATE:19650601T010000
RDATE:19660529T010000
RDATE:19670408T010000
RDATE:19680414T010000
RDATE:19690427T010000
RDATE:19700426T000000
RDATE:19710425T000000
RDATE:19720430T000000
RDATE:19730429T000000
RDATE:19740428T000000
RDATE:19750427T000000
RDATE:19760425T000000
RDATE:19770424T000000
RDATE:19780507T000000
RDATE:19790318T000000
RDATE:19800316T000000
RDATE:19810510T000000
RDATE:19820509T000000
RDATE:19830508T000000
RDATE:19840506T000000
RDATE:19850505T000000
RDATE:19860316T000000
RDATE:19870315T000000
RDATE:19880320T000000
RDATE:19890319T000000
RDATE:19900401T000000
RDATE:19910407T000000
RDATE:19920405T000000
RDATE:19930404T000000
RDATE:19940403T000000
RDATE:19950402T000000
RDATE:19960407T000000
RDATE:19970406T000000
RDATE:19980329T000000
RDATE:19990328T000000
RDATE:20000402T000000
RDATE:20010401T000000
RDATE:20020407T000000
RDATE:20030406T000000
RDATE:20040328T000000
RDATE:20070311T000000
RDATE:20080316T000000
RDATE:20090308T000000
RDATE:20100314T000000
RDATE:20110320T000000
RDATE:20120401T000000
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:CST
DTSTART:19281010T000000
RDATE:19281010T000000
RDATE:19400901T000000
RDATE:19410907T000000
RDATE:19420906T000000
RDATE:19450902T000000
RDATE:19460901T000000
RDATE:19650930T000000
RDATE:19661002T000000
RDATE:19670910T000000
RDATE:19680908T000000
RDATE:19691026T000000
RDATE:19701025T000000
RDATE:19711031T000000
RDATE:19721008T000000
RDATE:19731008T000000
RDATE:19741008T000000
RDATE:19751026T000000
RDATE:19761031T000000
RDATE:19771030T000000
RDATE:19781008T000000
RDATE:19791014T000000
RDATE:19801012T000000
RDATE:19811011T000000
RDATE:19821010T000000
RDATE:19831009T000000
RDATE:19841014T000000
RDATE:19851013T000000
RDATE:19861012T000000
RDATE:19871011T000000
RDATE:19881009T000000
RDATE:19891008T000000
RDATE:19901014T000000
RDATE:19911013T010000
RDATE:19921011T010000
RDATE:19931010T010000
RDATE:19941009T010000
RDATE:19951008T010000
RDATE:19961006T010000
RDATE:19971012T010000
RDATE:19981025T010000
RDATE:19991031T010000
RDATE:20001029T010000
RDATE:20011028T010000
RDATE:20021027T010000
RDATE:20031026T010000
RDATE:20061029T010000
RDATE:20071028T010000
RDATE:20081026T010000
RDATE:20091025T010000
RDATE:20101031T010000
RDATE:20111113T010000
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
";
            var vCal       = VCalendar.Parse(vCalString);

            var stringFromVCal = vCal.ToString();

            var vCalFromString = VCalendar.Parse(stringFromVCal);

            //check the vCalendar's properties
            Assert.True(checkProperties(vCal.Properties, vCalFromString.Properties));

            //check the calendar components
            foreach (var calComp in vCal.CalendarComponents)
            {
                Assert.Contains(calComp.Key, vCalFromString.CalendarComponents.Keys);

                Assert.Equal(calComp.Value.Count, vCalFromString.CalendarComponents[calComp.Key].Count);

                Assert.True(checkProperties(calComp.Value.First().Properties, vCalFromString.CalendarComponents[calComp.Key].First().Properties));
            }
        }
コード例 #11
0
        public void UnitTest3()
        {
            var calStr = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:[email protected]
ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:[email protected]
DTSTAMP:20060206T001220Z
DTSTART;TZID=US/Eastern:20060104T100000
DURATION:PT1H
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
LAST-MODIFIED:20060206T001330Z
ORGANIZER:mailto:[email protected]
SEQUENCE:1
STATUS:TENTATIVE
SUMMARY:Event #3
UID:[email protected]
X-ABC-GUID:[email protected]
END:VEVENT
END:VCALENDAR";

            var result = VCalendar.Parse(calStr);

            var xmlStr = @"
<C:calendar-data xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
<C:prop name=""DURATION""/>
<C:prop name=""RRULE""/>
<C:prop name=""ATTENDEE""/>
<C:prop name=""EXRULE""/>
<C:prop name=""EXDATE""/>
<C:prop name=""RECURRENCE-ID""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>";

            var calString = result.ToString(XmlTreeStructure.Parse(xmlStr));
        }
コード例 #12
0
        public void TestNullReferenceOnDurationProp()
        {
            var vCalString = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:+//IDN bitfire.at//DAVdroid/1.0.8 ical4android ical4j/2.x
BEGIN:VEVENT
DTSTAMP:20160907T213117Z
UID:1d2a9f2f-40d0-4733-b81b-1ea15d79ee21
DTSTART;TZID=America/Havana:20160905T080000
DURATION:PT5700S
RRULE:FREQ=WEEKLY;WKST=SU;COUNT=16;BYDAY=MO
SUMMARY:Programación y algoritmos CF
LOCATION:Aula 8
STATUS:CONFIRMED
END:VEVENT
BEGIN:VTIMEZONE
TZID:America/Havana
TZURL:http://tzurl.org/zoneinfo/America/Havana
X-LIC-LOCATION:America/Havana
BEGIN:STANDARD
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:CST
DTSTART:20121104T010000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:CDT
DTSTART:20130310T000000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-052928
TZOFFSETTO:-052936
TZNAME:HMT
DTSTART:18900101T000000
RDATE:18900101T000000
END:STANDARD
BEGIN:STANDARD
TZOFFSETFROM:-052936
TZOFFSETTO:-0500
TZNAME:CST
DTSTART:19250719T120000
RDATE:19250719T120000
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:CDT
DTSTART:19280610T010000
RDATE:19280610T010000
RDATE:19400602T010000
RDATE:19410601T010000
RDATE:19420607T010000
RDATE:19450603T010000
RDATE:19460602T010000
RDATE:19650601T010000
RDATE:19660529T010000
RDATE:19670408T010000
RDATE:19680414T010000
RDATE:19690427T010000
RDATE:19700426T000000
RDATE:19710425T000000
RDATE:19720430T000000
RDATE:19730429T000000
RDATE:19740428T000000
RDATE:19750427T000000
RDATE:19760425T000000
RDATE:19770424T000000
RDATE:19780507T000000
RDATE:19790318T000000
RDATE:19800316T000000
RDATE:19810510T000000
RDATE:19820509T000000
RDATE:19830508T000000
RDATE:19840506T000000
RDATE:19850505T000000
RDATE:19860316T000000
RDATE:19870315T000000
RDATE:19880320T000000
RDATE:19890319T000000
RDATE:19900401T000000
RDATE:19910407T000000
RDATE:19920405T000000
RDATE:19930404T000000
RDATE:19940403T000000
RDATE:19950402T000000
RDATE:19960407T000000
RDATE:19970406T000000
RDATE:19980329T000000
RDATE:19990328T000000
RDATE:20000402T000000
RDATE:20010401T000000
RDATE:20020407T000000
RDATE:20030406T000000
RDATE:20040328T000000
RDATE:20070311T000000
RDATE:20080316T000000
RDATE:20090308T000000
RDATE:20100314T000000
RDATE:20110320T000000
RDATE:20120401T000000
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:CST
DTSTART:19281010T000000
RDATE:19281010T000000
RDATE:19400901T000000
RDATE:19410907T000000
RDATE:19420906T000000
RDATE:19450902T000000
RDATE:19460901T000000
RDATE:19650930T000000
RDATE:19661002T000000
RDATE:19670910T000000
RDATE:19680908T000000
RDATE:19691026T000000
RDATE:19701025T000000
RDATE:19711031T000000
RDATE:19721008T000000
RDATE:19731008T000000
RDATE:19741008T000000
RDATE:19751026T000000
RDATE:19761031T000000
RDATE:19771030T000000
RDATE:19781008T000000
RDATE:19791014T000000
RDATE:19801012T000000
RDATE:19811011T000000
RDATE:19821010T000000
RDATE:19831009T000000
RDATE:19841014T000000
RDATE:19851013T000000
RDATE:19861012T000000
RDATE:19871011T000000
RDATE:19881009T000000
RDATE:19891008T000000
RDATE:19901014T000000
RDATE:19911013T010000
RDATE:19921011T010000
RDATE:19931010T010000
RDATE:19941009T010000
RDATE:19951008T010000
RDATE:19961006T010000
RDATE:19971012T010000
RDATE:19981025T010000
RDATE:19991031T010000
RDATE:20001029T010000
RDATE:20011028T010000
RDATE:20021027T010000
RDATE:20031026T010000
RDATE:20061029T010000
RDATE:20071028T010000
RDATE:20081026T010000
RDATE:20091025T010000
RDATE:20101031T010000
RDATE:20111113T010000
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
";
            var vCal       = VCalendar.Parse(vCalString);

            var stringFromVCal = vCal.ToString();

            Assert.NotNull(stringFromVCal);
        }
コード例 #13
0
 /// <summary>
 /// Constructor
 /// </summary>
 public AppointmentSender()
     : base()
 {
     AppointmentInfo = new VCalendar();
 }
コード例 #14
0
ファイル: FreeBusyDetails.aspx.cs プロジェクト: ywscr/PDI
        /// <summary>
        /// Save changes and return to the calendar browser
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event arguments</param>
        protected void btnSave_Click(object sender, EventArgs e)
        {
            DateTime startDate = DateTime.MinValue, endDate = DateTime.MinValue;
            Duration dur = Duration.Zero;

            if (!Page.IsValid)
            {
                return;
            }

            lblMsg.Text = null;

            // Perform some edits
            if (txtStartDate.Text.Trim().Length != 0 && !DateTime.TryParse(txtStartDate.Text,
                                                                           CultureInfo.CurrentCulture, DateTimeStyles.None, out startDate))
            {
                lblMsg.Text = "Invalid start date format<br>";
            }

            if (txtEndDate.Text.Trim().Length != 0 && !DateTime.TryParse(txtEndDate.Text,
                                                                         CultureInfo.CurrentCulture, DateTimeStyles.None, out endDate))
            {
                lblMsg.Text += "Invalid end date format<br>";
            }

            if (txtDuration.Text.Trim().Length != 0 && !Duration.TryParse(txtDuration.Text, out dur))
            {
                lblMsg.Text += "Invalid duration format<br>";
            }

            if (startDate != DateTime.MinValue && endDate != DateTime.MinValue && startDate > endDate)
            {
                lblMsg.Text += "Start date must be less than or equal to end date<br>";
            }

            if (!String.IsNullOrWhiteSpace(lblMsg.Text))
            {
                return;
            }

            VCalendar cal = (VCalendar)Session["VCalendar"];

            // Not very friendly, but it's just a demo
            if (cal == null)
            {
                Response.Redirect("CalendarBrowser.aspx");
                return;
            }

            VFreeBusy fb = cal.FreeBusys[(int)this.ViewState["FreeBusyIndex"]];

            // The unique ID is not changed
            fb.Organizer.Value = txtOrganizer.Text;
            fb.Contact.Value   = txtContact.Text;

            // We'll use the TimeZoneDateTime property on all date/time values so that they are set literally
            // rather than being converted to the time zone as would happen with the DateTimeValue property.
            fb.StartDateTime.TimeZoneDateTime = startDate;
            fb.StartDateTime.ValueLocation    = ValLocValue.DateTime;

            fb.EndDateTime.TimeZoneDateTime = endDate;
            fb.StartDateTime.ValueLocation  = ValLocValue.DateTime;

            fb.Duration.DurationValue = dur;
            fb.Url.Value     = txtUrl.Text;
            fb.Comment.Value = txtComments.Text;

            Response.Redirect("CalendarBrowser.aspx");
        }
コード例 #15
0
        /// <summary>
        /// This is used to custom draw the start date/time and summary columns based on the information
        /// available.
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event arguments</param>
        private void dgvCalendar_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
        {
            CurrencyManager     cm;
            StartDateProperty   startProp;
            SummaryProperty     summaryProp;
            DescriptionProperty descProp;
            DateTimeInstance    dti;

            Color  foreColor;
            object item;
            string columnText = null;

            if (e.RowIndex > -1 && (e.ColumnIndex == 0 || e.ColumnIndex == 1))
            {
                cm   = (CurrencyManager)dgvCalendar.BindingContext[dgvCalendar.DataSource];
                item = cm.List[e.RowIndex];

                if (e.ColumnIndex == 0)
                {
                    if (item is VEvent)
                    {
                        startProp = ((VEvent)item).StartDateTime;
                    }
                    else
                    if (item is VToDo)
                    {
                        startProp = ((VToDo)item).StartDateTime;
                    }
                    else
                    if (item is VJournal)
                    {
                        startProp = ((VJournal)item).StartDateTime;
                    }
                    else
                    {
                        startProp = ((VFreeBusy)item).StartDateTime;
                    }

                    dti = VCalendar.TimeZoneTimeInfo(startProp.TimeZoneDateTime, startProp.TimeZoneId);

                    // Format as date or date/time and include time zone if available
                    if (startProp.ValueLocation == ValLocValue.Date)
                    {
                        columnText = String.Format("{0:d} {1}", dti.StartDateTime, dti.AbbreviatedStartTimeZoneName);
                    }
                    else
                    {
                        columnText = String.Format("{0} {1}", dti.StartDateTime, dti.AbbreviatedStartTimeZoneName);
                    }
                }
                else
                if (e.ColumnIndex == 1)
                {
                    if (item is VEvent)
                    {
                        summaryProp = ((VEvent)item).Summary;
                        descProp    = ((VEvent)item).Description;
                    }
                    else
                    if (item is VToDo)
                    {
                        summaryProp = ((VToDo)item).Summary;
                        descProp    = ((VToDo)item).Description;
                    }
                    else
                    if (item is VJournal)
                    {
                        summaryProp = ((VJournal)item).Summary;
                        descProp    = ((VJournal)item).Description;
                    }
                    else
                    {
                        summaryProp = null;
                        descProp    = null;
                    }

                    // If summary is empty, use description instead
                    if (summaryProp != null && summaryProp.Value != null)
                    {
                        columnText = summaryProp.Value;
                    }
                    else
                    if (descProp != null)
                    {
                        columnText = descProp.Value;
                    }
                }

                if (columnText != null)
                {
                    // If multi-line, limit it to the first line
                    if (columnText.IndexOf('\n') != -1)
                    {
                        columnText = columnText.Substring(0, columnText.IndexOf('\n'));
                    }

                    e.Paint(e.CellBounds, e.PaintParts & ~DataGridViewPaintParts.ContentForeground);

                    // Based the foreground color on the selected state
                    if ((e.State & DataGridViewElementStates.Selected) != 0)
                    {
                        foreColor = e.CellStyle.SelectionForeColor;
                    }
                    else
                    {
                        foreColor = e.CellStyle.ForeColor;
                    }

                    using (SolidBrush b = new SolidBrush(foreColor))
                    {
                        e.Graphics.DrawString(columnText, e.CellStyle.Font, b, e.CellBounds, sf);
                    }

                    e.Handled = true;
                }
            }
        }
コード例 #16
0
ファイル: EventRecurTestForm.cs プロジェクト: ywscr/PDI
        /// <summary>
        /// Test the recurrence within the specified iCalendar component
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event arguments</param>
        private void btnTest_Click(object sender, EventArgs e)
        {
            RecurringObject            ro = null;
            DateTimeInstanceCollection instances;
            string calendar;
            int    start;
            double elapsed;

            try
            {
                lblCount.Text = null;

                // Wrap it in VCALENDAR tags and parse it
                calendar = String.Format("BEGIN:VCALENDAR\nVERSION:2.0\n{0}\nEND:VCALENDAR", txtCalendar.Text);

                VCalendar cal = VCalendarParser.ParseFromString(calendar);

                // Get the first event, to-do, or journal item
                if (cal.Events.Count > 0)
                {
                    ro = cal.Events[0];
                }
                else
                if (cal.ToDos.Count > 0)
                {
                    ro = cal.ToDos[0];
                }
                else
                if (cal.Journals.Count > 0)
                {
                    ro = cal.Journals[0];
                }

                if (ro == null)
                {
                    lblCount.Text = "No event, to-do, or journal item found";
                    return;
                }

                // Apply the time zone to the calendar.  If "None" is selected, the time zone will be cleared on
                // all items.
                if (cboTimeZone.SelectedIndex < 1)
                {
                    cal.ApplyTimeZone(null);
                }
                else
                {
                    cal.ApplyTimeZone(VCalendar.TimeZones[cboTimeZone.SelectedIndex - 1]);
                }

                txtCalendar.Text = ro.ToString();

                lbDates.Items.Clear();
                this.Cursor = Cursors.WaitCursor;

                start     = System.Environment.TickCount;
                instances = ro.InstancesBetween(dtpStartDate.Value, dtpEndDate.Value, chkInLocalTime.Checked);
                elapsed   = (System.Environment.TickCount - start) / 1000.0;

                cal.Dispose();

                // The date instance contains the start and end date/times, the duration, and time zone
                // information.  The duration is based on the duration of the calendar component.  The time
                // zone information is based on the "In Local Time" parameter of the InstancesBetween() method
                // and whether or not the component has a Time Zone ID specified.
                foreach (DateTimeInstance dti in instances)
                {
                    lbDates.Items.Add(String.Format("{0:G} {1} to {2:G} {3} ({4})", dti.StartDateTime,
                                                    dti.AbbreviatedStartTimeZoneName, dti.EndDateTime, dti.AbbreviatedEndTimeZoneName,
                                                    dti.Duration.ToDescription()));

                    if (lbDates.Items.Count > 5000)
                    {
                        lblCount.Text += "A large number of instances were returned.  Only the first 5000 " +
                                         "have been loaded into the list box.\r\n";
                        break;
                    }
                }

                // If nothing was found remind the user that they may need to adjust the start and end date range
                // to find stuff within the item.
                if (instances.Count == 0)
                {
                    MessageBox.Show("Nothing found.  If this was unexpected, check the limiting date range in " +
                                    "the two date/time text boxes at the top of the form and the calendar item date/time " +
                                    "properties to make sure that they do overlap");
                }

                lblCount.Text += String.Format("Found {0:N0} instances in {1:N2} seconds ({2:N2} instances/second)",
                                               instances.Count, elapsed, instances.Count / elapsed);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }
コード例 #17
0
        /// <summary>
        /// Generate instances for the specified component
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event parameters</param>
        protected void btnTest_Click(object sender, EventArgs e)
        {
            RecurringObject            ro = null;
            DateTimeInstanceCollection instances;
            string   calendar;
            int      start;
            double   elapsed;
            DateTime startDate, endDate;

            try
            {
                lblCount.Text = String.Empty;

                if (!DateTime.TryParse(txtStartDate.Text, CultureInfo.CurrentCulture, DateTimeStyles.None,
                                       out startDate) || !DateTime.TryParse(txtEndDate.Text, CultureInfo.CurrentCulture,
                                                                            DateTimeStyles.None, out endDate))
                {
                    lblCount.Text = "Invalid start or end date/time format";
                    return;
                }

                // Wrap it in VCALENDAR tags and parse it
                calendar = String.Format("BEGIN:VCALENDAR\nVERSION:2.0\n{0}\nEND:VCALENDAR", txtCalendar.Text);

                VCalendar cal = VCalendarParser.ParseFromString(calendar);

                // Get the first event, to-do, or journal item
                if (cal.Events.Count > 0)
                {
                    ro = cal.Events[0];
                }
                else
                if (cal.ToDos.Count > 0)
                {
                    ro = cal.ToDos[0];
                }
                else
                if (cal.Journals.Count > 0)
                {
                    ro = cal.Journals[0];
                }

                if (ro == null)
                {
                    lblCount.Text = "No event, to-do, or journal item found";
                    return;
                }

                // Apply the time zone to the calendar.  If "None" is selected, the time zone will be cleared on
                // all items.
                if (cboTimeZone.SelectedIndex < 1)
                {
                    cal.ApplyTimeZone(null);
                }
                else
                {
                    cal.ApplyTimeZone(VCalendar.TimeZones[cboTimeZone.SelectedIndex - 1]);
                }

                foreach (RRuleProperty rrule in ro.RecurrenceRules)
                {
                    ApplyLimits(ro, rrule.Recurrence);
                }

                foreach (ExRuleProperty exrule in ro.ExceptionRules)
                {
                    ApplyLimits(ro, exrule.Recurrence);
                }

                txtCalendar.Text = ro.ToString();

                start     = System.Environment.TickCount;
                instances = ro.InstancesBetween(startDate, endDate, chkInLocalTime.Checked);
                elapsed   = (System.Environment.TickCount - start) / 1000.0;

                cal.Dispose();

                // The date instance contains the start and end date/times, the duration, and time zone
                // information.  The duration is based on the duration of the calendar component.  The time zone
                // information is based on the "In Local Time" parameter of the InstancesBetween() method and
                // whether or not the component has a Time Zone ID specified.
                dlDates.DataSource = instances;
                dlDates.DataBind();

                // If nothing was found remind the user that they may need to adjust the start and end date range
                // to find stuff within the item.
                if (instances.Count == 0)
                {
                    lblCount.Text = "Nothing found.  If this was unexpected, check the limiting date range " +
                                    "in the two date/time text boxes at the top of the form and the calendar item date/time " +
                                    "properties to make sure that they do overlap<br/><br/>";
                }

                lblCount.Text += String.Format("Found {0:N0} instances in {1:N2} seconds ({2:N2} instances/second)",
                                               instances.Count, elapsed, instances.Count / elapsed);
            }
            catch (Exception ex)
            {
                lblCount.Text = ex.Message;
            }
        }
コード例 #18
0
ファイル: Class1.cs プロジェクト: tleviathan/ICalendar
        public void ToStringTest()
        {
            var calString = @"BEGIN:VCALENDAR
            PRODID:-//Google Inc//Google Calendar 70.9054//EN
            VERSION:2.0
            CALSCALE:GREGORIAN
            X-WR-CALNAME:[email protected]
            X-WR-TIMEZONE:America/Los_Angeles
            BEGIN:VTIMEZONE
            TZID:America/Los_Angeles
            X-LIC-LOCATION:America/Los_Angeles
            BEGIN:DAYLIGHT
            TZOFFSETFROM:-0800
            TZOFFSETTO:-0700
            TZNAME:PDT
            DTSTART:19700308T020000
            RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
            END:DAYLIGHT
            BEGIN:STANDARD
            TZOFFSETFROM:-0700
            TZOFFSETTO:-0800
            TZNAME:PST
            DTSTART:19701101T020000
            RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
            END:STANDARD
            END:VTIMEZONE
            BEGIN:VEVENT
            DTSTART;TZID=America/Los_Angeles:20120629T130000
            DTEND;TZID=America/Los_Angeles:20120629T140000
            DTSTAMP:20120629T112428Z
            UID:[email protected]
            RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
            CREATED:20120629T111935Z
            DESCRIPTION:foo
            LAST-MODIFIED:20120629T112428Z
            LOCATION:Barcelona
            SEQUENCE:0
            STATUS:CONFIRMED
            SUMMARY:Demo B2G Calendar
            TRANSP:OPAQUE
            BEGIN:VALARM
            ACTION:EMAIL
            DESCRIPTION:This is an event reminder
            SUMMARY:Alarm notification
            ATTENDEE:mailto:[email protected]
            TRIGGER:-P0DT0H30M0S
            END:VALARM
            BEGIN:VALARM
            ACTION:DISPLAY
            DESCRIPTION:This is an event reminder
            TRIGGER:-P0DT0H30M0S
            END:VALARM
            END:VEVENT
            END:VCALENDAR
            ";
            VCalendar calendar = VCalendar.Parse(calString);
            var xmlDoc = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
            <C:calendar-query xmlns:D=""DAV:""
            xmlns:C=""urn:ietf:params:xml:ns:caldav"">
            <D:prop>
            <D:getetag/>
            <C:calendar-data>
            <C:comp name=""VCALENDAR"">
            <C:prop name=""VERSION""/>
            <C:comp name=""VEVENT"">
            <C:prop name=""SUMMARY""/>
            <C:prop name=""UID""/>
            <C:prop name=""DTSTART""/>
            <C:prop name=""DTEND""/>
            </C:comp>
            <C:comp name=""VTIMEZONE""/>
            </C:comp>
            </C:calendar-data>
            </D:prop>
            <C:filter>
            <C:comp-filter name=""VCALENDAR"">
            <C:comp-filter name=""VEVENT"">
            <C:time-range start=""20060104T000000Z""
            end=""20060105T000000Z""/>
            </C:comp-filter>
            </C:comp-filter>
            </C:filter>
            </C:calendar-query>";
            var xmlTree = XmlTreeStructure.Parse(xmlDoc);
            var node = xmlTree.GetChildAtAnyLevel("calendar-data");
            var newCalString = calendar.ToString(node);
            var newCal = new VCalendar(newCalString);

            Assert.Equal(2, newCal.CalendarComponents.Count);
            Assert.Contains("VEVENT", newCal.CalendarComponents.Keys);
            Assert.Contains("VTIMEZONE", newCal.CalendarComponents.Keys);
            Assert.Equal(4, newCal.CalendarComponents["VEVENT"].First().Properties.Count);
        }
コード例 #19
0
ファイル: VFreeBusyDlg.cs プロジェクト: nhaberl/PDI
        /// <summary>
        /// This is used to apply the time zone selection to all date/time values in the dialog box
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">The event parameters</param>
        private void btnApplyTZ_Click(object sender, EventArgs e)
        {
            DateTimeInstance dti;
            string           sourceTZ, destTZ;

            if (cboTimeZone.SelectedIndex == timeZoneIdx)
            {
                MessageBox.Show("The time zone has not changed", "No Change", MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
                return;
            }

            if (MessageBox.Show(String.Format("Do you want to convert all times from the '{0}' time zone to " +
                                              "the '{1}' time zone?", cboTimeZone.Items[timeZoneIdx], cboTimeZone.Items[cboTimeZone.SelectedIndex]),
                                "Change Time Zone", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
            {
                return;
            }

            // Get the time zone IDs
            if (timeZoneIdx == 0)
            {
                sourceTZ = null;
            }
            else
            {
                sourceTZ = (string)cboTimeZone.Items[timeZoneIdx];
            }

            destTZ = (string)cboTimeZone.Items[cboTimeZone.SelectedIndex];

            // Convert the times
            if (sourceTZ == null)
            {
                if (dtpStartDate.Checked)
                {
                    dti = VCalendar.LocalTimeToTimeZoneTime(dtpStartDate.Value, destTZ);
                    dtpStartDate.Value = dti.StartDateTime;
                }

                if (dtpEndDate.Checked)
                {
                    dti = VCalendar.LocalTimeToTimeZoneTime(dtpEndDate.Value, destTZ);
                    dtpEndDate.Value = dti.StartDateTime;
                }
            }
            else
            {
                if (dtpStartDate.Checked)
                {
                    dti = VCalendar.TimeZoneToTimeZone(dtpStartDate.Value, sourceTZ, destTZ);
                    dtpStartDate.Value = dti.StartDateTime;
                }

                if (dtpEndDate.Checked)
                {
                    dti = VCalendar.TimeZoneToTimeZone(dtpEndDate.Value, sourceTZ, destTZ);
                    dtpEndDate.Value = dti.StartDateTime;
                }
            }

            ucFreeBusy.ApplyTimeZone(sourceTZ, destTZ);

            timeZoneIdx = cboTimeZone.SelectedIndex;
        }
コード例 #20
0
    public void Returns_Correct_VCalendar()
    {
        // Arrange
        var e0              = new EventModel(Rnd.DateTime, Rnd.DateTime, false, Rnd.Str, Rnd.Str, Rnd.Str, false);
        var e1              = new EventModel(Rnd.DateTime, Rnd.DateTime, false, Rnd.Str, Rnd.Str, Rnd.Str, true);
        var events          = ImmutableList.Create(e0, e1);
        var lastModified    = Rnd.DateTime;
        var lastModifiedStr = VCalendar.Format(lastModified);
        var calendar        = new CalendarModel(events, lastModified);
        var tzid            = Rnd.Str;
        var domain          = Rnd.Str;
        var vcal            = new VCalendar(calendar, tzid);
        var expected        = new StringBuilder()
                              .AppendLine("BEGIN:VCALENDAR")
                              .AppendLine("VERSION:2.0")
                              .AppendLine("PRODID:-//bfren//NONSGML Jeebs.Calendar//EN")
                              .AppendLine("CALSCALE:GREGORIAN")
                              .AppendLine("X-PUBLISHED-TTL:PT1H")
                              .AppendLine("BEGIN:VTIMEZONE")
                              .AppendLine("TZID:Europe/London")
                              .AppendLine("BEGIN:STANDARD")
                              .AppendLine("TZNAME:GMT")
                              .AppendLine("DTSTART:19710101T020000")
                              .AppendLine("TZOFFSETFROM:+0100")
                              .AppendLine("TZOFFSETTO:+0000")
                              .AppendLine("RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU")
                              .AppendLine("END:STANDARD")
                              .AppendLine("BEGIN:DAYLIGHT")
                              .AppendLine("TZNAME:BST")
                              .AppendLine("DTSTART:19710101T010000")
                              .AppendLine("TZOFFSETFROM:+0000")
                              .AppendLine("TZOFFSETTO:+0100")
                              .AppendLine("RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU")
                              .AppendLine("END:DAYLIGHT")
                              .AppendLine("END:VTIMEZONE")
                              .AppendLine("BEGIN:VEVENT")
                              .AppendLine($"UID:{lastModifiedStr}-000000@{domain}")
                              .AppendLine($"CREATED:{lastModifiedStr}")
                              .AppendLine($"LAST-MODIFIED:{lastModifiedStr}")
                              .AppendLine($"DTSTAMP:{lastModifiedStr}")
                              .AppendLine($"SUMMARY:{e0.Summary}")
                              .AppendLine($"DESCRIPTION:{e0.Description}")
                              .AppendLine($"LOCATION:{e0.Location}")
                              .AppendLine($"DTSTART;TZID={tzid}:{VCalendar.Format(e0.Start)}")
                              .AppendLine($"DTEND;TZID={tzid}:{VCalendar.Format(e0.End)}")
                              .AppendLine("END:VEVENT")
                              .AppendLine("BEGIN:VEVENT")
                              .AppendLine($"UID:{lastModifiedStr}-000001@{domain}")
                              .AppendLine($"CREATED:{lastModifiedStr}")
                              .AppendLine($"LAST-MODIFIED:{lastModifiedStr}")
                              .AppendLine($"DTSTAMP:{lastModifiedStr}")
                              .AppendLine($"SUMMARY:{e1.Summary}")
                              .AppendLine($"DESCRIPTION:{e1.Description}")
                              .AppendLine($"LOCATION:{e1.Location}")
                              .AppendLine($"DTSTART;TZID={tzid}:{VCalendar.Format(e1.Start)}")
                              .AppendLine($"DTEND;TZID={tzid}:{VCalendar.Format(e1.End)}")
                              .AppendLine("TRANSP:TRANSPARENT")
                              .AppendLine("END:VEVENT")
                              .AppendLine("END:VCALENDAR")
                              .ToString();

        // Act
        var result = vcal.ToString(domain);

        // Assert
        Assert.Equal(expected, result);
    }