/// <summary> /// Gets the occurrences. /// </summary> /// <param name="recurrable">The recurrable.</param> /// <param name="periodStart">The period start.</param> /// <param name="periodEnd">The period end.</param> /// <param name="includeReferenceDateInResults"> /// if set to <c>true</c> [include reference date in results]. /// </param> /// <returns></returns> public static IList <Occurrence> GetOccurrences(IRecurrable recurrable, IDateTime periodStart, IDateTime periodEnd, bool includeReferenceDateInResults) { var occurrences = new List <Occurrence>( ); var evaluator = recurrable.GetService(typeof(IEvaluator)) as IEvaluator; if (evaluator != null) { // Ensure the start time is associated with the object being queried var start = recurrable.Start.Copy <IDateTime>( ); start.AssociatedObject = recurrable as ICalendarObject; // Change the time zone of periodStart/periodEnd as needed // so they can be used during the evaluation process. periodStart = DateUtil.MatchTimeZone(start, periodStart); periodEnd = DateUtil.MatchTimeZone(start, periodEnd); ISet <IPeriod> periods = evaluator.Evaluate( start, DateUtil.GetSimpleDateTimeData(periodStart), DateUtil.GetSimpleDateTimeData(periodEnd), includeReferenceDateInResults); // Filter the resulting periods to only contain those // that occur sometime between startTime and endTime. // NOTE: fixes bug #3007244 - GetOccurences not returning long spanning all-day events occurrences.AddRange(from p in periods let endTime = p.EndTime ?? p.StartTime where endTime.GreaterThan(periodStart) && p.StartTime.LessThanOrEqual(periodEnd) select new Occurrence(recurrable, p)); occurrences.Sort( ); } return(occurrences); }
/// <summary> /// Evaluates to previous occurrence. /// </summary> /// <param name="completedDate">The completed date.</param> /// <param name="currentDateTime">The current date time.</param> public void EvaluateToPreviousOccurrence(IDateTime completedDate, IDateTime currentDateTime) { var beginningDate = completedDate.Copy <IDateTime>( ); if (Todo.RecurrenceRules != null) { foreach (IRecurrencePattern rrule in Todo.RecurrenceRules) { DetermineStartingRecurrence(rrule, ref beginningDate); } } if (Todo.RecurrenceDates != null) { foreach (IPeriodList rdate in Todo.RecurrenceDates) { DetermineStartingRecurrence(rdate, ref beginningDate); } } if (Todo.ExceptionRules != null) { foreach (IRecurrencePattern exrule in Todo.ExceptionRules) { DetermineStartingRecurrence(exrule, ref beginningDate); } } if (Todo.ExceptionDates != null) { foreach (IPeriodList exdate in Todo.ExceptionDates) { DetermineStartingRecurrence(exdate, ref beginningDate); } } Evaluate(Todo.Start, DateUtil.GetSimpleDateTimeData(beginningDate), DateUtil.GetSimpleDateTimeData(currentDateTime).AddTicks(1), true); }
/// <summary> /// Returns the observance that corresponds to /// the date/time provided, or null if no matching /// observance could be found within this TimeZoneInfo. /// </summary> /// <param name="dt"></param> /// <returns></returns> /// <exception cref="System.Exception">Cannot call GetObservance() on a TimeZoneInfo whose Parent property is null.</exception> public TimeZoneObservance?GetObservance(IDateTime dt) { if (Parent == null) { throw new Exception("Cannot call GetObservance() on a TimeZoneInfo whose Parent property is null."); } if (string.Equals(dt.TzId, TzId)) { // Normalize date/time values within this time zone to a local value. DateTime normalizedDt = dt.Value; // Let's evaluate our time zone observances to find the // observance that applies to this date/time value. var parentEval = Parent.GetService(typeof(IEvaluator)) as IEvaluator; if (parentEval != null) { // Evaluate the date/time in question. parentEval.Evaluate(Start, DateUtil.GetSimpleDateTimeData(Start), normalizedDt, true); // NOTE: We avoid using period.Contains here, because we want to avoid // doing an inadvertent time zone lookup with it. IPeriod period = _evaluator .Periods .FirstOrDefault(p => p.StartTime.Value <= normalizedDt && p.EndTime.Value > normalizedDt ); if (period != null) { return(new TimeZoneObservance(period, this)); } } } return(null); }
/// <summary> /// Gets the dates. /// </summary> /// <param name="seed">The seed.</param> /// <param name="periodStart">The period start.</param> /// <param name="periodEnd">The period end.</param> /// <param name="maxCount">The max count.</param> /// <param name="pattern">The pattern.</param> /// <param name="includeReferenceDateInResults"> /// if set to <c>true</c> [include reference date in results]. /// </param> /// <returns></returns> private IEnumerable <DateTime> GetDates(IDateTime seed, DateTime periodStart, DateTime periodEnd, int maxCount, IRecurrencePattern pattern, bool includeReferenceDateInResults) { var dates = new List <DateTime>( ); DateTime originalDate = DateUtil.GetSimpleDateTimeData(seed); DateTime seedCopy = DateUtil.GetSimpleDateTimeData(seed); if (includeReferenceDateInResults) { dates.Add(seedCopy); } // If the interval is set to zero, or our count prevents us // from getting additional items, then return with the reference // date only. if (pattern.Interval == 0 || (pattern.Count != int.MinValue && pattern.Count <= dates.Count)) { return(dates); } // optimize the start time for selecting candidates // (only applicable where a COUNT is not specified) if (pattern.Count == int.MinValue) { DateTime incremented = seedCopy; // FIXME: we can more aggressively increment here when // the difference between dates is greater. IncrementDate(ref incremented, pattern, pattern.Interval); while (incremented < periodStart) { seedCopy = incremented; IncrementDate(ref incremented, pattern, pattern.Interval); } } bool?[] expandBehavior = RecurrenceUtil.GetExpandBehaviorList(pattern); int invalidCandidateCount = 0; int noCandidateIncrementCount = 0; DateTime candidate = DateTime.MinValue; while ((maxCount < 0) || (dates.Count < maxCount)) { if (pattern.Until != DateTime.MinValue && candidate != DateTime.MinValue && candidate > pattern.Until) { break; } if (candidate != DateTime.MinValue && candidate > periodEnd) { break; } if (pattern.Count >= 1 && (dates.Count + invalidCandidateCount) >= pattern.Count) { break; } List <DateTime> candidates = GetCandidates(seedCopy, pattern, expandBehavior); if (candidates.Count > 0) { noCandidateIncrementCount = 0; // sort candidates for identifying when UNTIL date is exceeded.. candidates.Sort( ); foreach (DateTime t in candidates) { candidate = t; // don't count candidates that occur before the original date.. if (candidate >= originalDate) { // candidates MAY occur before periodStart // For example, FREQ=YEARLY;BYWEEKNO=1 could return dates // from the previous year. // // candidates exclusive of periodEnd.. if (candidate >= periodEnd) { invalidCandidateCount++; } else if (pattern.Count >= 1 && (dates.Count + invalidCandidateCount) >= pattern.Count) { break; } else if (pattern.Until == DateTime.MinValue || candidate <= pattern.Until) { if (!dates.Contains(candidate)) { dates.Add(candidate); } } } } } else { noCandidateIncrementCount++; if ((noCandidateIncrementCount > MaxIncrementCount)) { break; } } IncrementDate(ref seedCopy, pattern, pattern.Interval); } // sort final list.. dates.Sort( ); return(dates); }