示例#1
0
        /// <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 || !excludeStart))
            {
                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
        }
示例#2
0
        /// <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
        }