} // Difference // ---------------------------------------------------------------------- public TimeSpan Difference(DateTime date1, DateTime date2) { if (date1.Equals(date2)) { return(TimeSpan.Zero); } if (collectorFilter.WeekDays.Count == 0 && collectorFilter.CollectingHours.Count == 0 && collectorFilter.CollectingDayHours.Count == 0) { return(new DateDiff(date1, date2, calendar.Culture.Calendar, calendar.FirstDayOfWeek, calendar.YearBaseMonth).Difference); } TimeRange differenceRange = new TimeRange(date1, date2); CalendarPeriodCollector collector = new CalendarPeriodCollector( collectorFilter, new TimeRange(differenceRange.Start.Date, differenceRange.End.Date.AddDays(1)), SeekDirection.Forward, calendar); collector.CollectHours(); // calculate gaps TimeGapCalculator <TimeRange> gapCalculator = new TimeGapCalculator <TimeRange>(calendar); ITimePeriodCollection gaps = gapCalculator.GetGaps(collector.Periods, differenceRange); // calculate difference (sum gap durations) TimeSpan difference = gaps.GetTotalDuration(durationProvider); return(date1 < date2 ? difference : difference.Negate()); } // Difference
} // TimePeriodSubtractor // ---------------------------------------------------------------------- public TimePeriodSubtractor(ITimePeriodMapper periodMapper) { this.periodMapper = periodMapper; timePeriodCombiner = new TimePeriodCombiner <T>(periodMapper); timeGapCalculator = new TimeGapCalculator <T>(periodMapper); timePeriodIntersector = new TimePeriodIntersector <T>(periodMapper); } // TimePeriodSubtractor
} // FindNextWeek // ---------------------------------------------------------------------- private Week FindPreviousWeek(Week current) { if (ExcludePeriods.Count == 0) { return(current.GetPreviousWeek()); } TimeRange limits = new TimeRange(DateTime.MinValue, current.Start.AddTicks(-1)); TimeGapCalculator <TimeRange> gapCalculator = new TimeGapCalculator <TimeRange>(calendar); ITimePeriodCollection remainingPeriods = gapCalculator.GetGaps(ExcludePeriods, limits); return(remainingPeriods.Count > 0 ? new Week(remainingPeriods[remainingPeriods.Count - 1].End) : null); } // FindPreviousWeek
} // CalculateEnd // ---------------------------------------------------------------------- private Week FindNextWeek(Week current) { if (ExcludePeriods.Count == 0) { return(current.GetNextWeek()); } TimeRange limits = new TimeRange(current.End.AddTicks(1), DateTime.MaxValue); TimeGapCalculator <TimeRange> gapCalculator = new TimeGapCalculator <TimeRange>(calendar); ITimePeriodCollection remainingPeriods = gapCalculator.GetGaps(ExcludePeriods, limits); return(remainingPeriods.Count > 0 ? new Week(remainingPeriods[0].Start) : null); } // FindNextWeek
} // Add // ---------------------------------------------------------------------- protected DateTime?CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode, out TimeSpan?remaining) { if (offset < TimeSpan.Zero) { throw new InvalidOperationException("time span must be positive"); } remaining = offset; // search periods TimePeriodCollection searchPeriods = new TimePeriodCollection(includePeriods); // no search periods specified: search anytime if (searchPeriods.Count == 0) { searchPeriods.Add(TimeRange.Anytime); } // available periods ITimePeriodCollection availablePeriods = new TimePeriodCollection(); // no exclude periods specified: use all search periods if (excludePeriods.Count == 0) { availablePeriods.AddAll(searchPeriods); } else // remove exclude periods { TimeGapCalculator <TimeRange> gapCalculator = new TimeGapCalculator <TimeRange>(); foreach (ITimePeriod searchPeriod in searchPeriods) { // no overlaps: use the entire search range if (!excludePeriods.HasOverlapPeriods(searchPeriod)) { availablePeriods.Add(searchPeriod); } else // add gaps of search period using the exclude periods { availablePeriods.AddAll(gapCalculator.GetGaps(excludePeriods, searchPeriod)); } } } // no periods available if (availablePeriods.Count == 0) { return(null); } // combine the available periods, ensure no overlapping // used for FindNextPeriod/FindPreviousPeriod if (availablePeriods.Count > 1) { TimePeriodCombiner <TimeRange> periodCombiner = new TimePeriodCombiner <TimeRange>(); availablePeriods = periodCombiner.CombinePeriods(availablePeriods); } // find the starting search period ITimePeriod startPeriod = null; DateTime seekMoment = start; switch (seekDirection) { case SeekDirection.Forward: startPeriod = FindNextPeriod(start, availablePeriods, out seekMoment); break; case SeekDirection.Backward: startPeriod = FindPreviousPeriod(start, availablePeriods, out seekMoment); break; } // no starting period available if (startPeriod == null) { return(null); } // no offset: use the search staring position // maybe moved to the next available search period if (offset == TimeSpan.Zero) { return(seekMoment); } // setup destination search switch (seekDirection) { case SeekDirection.Forward: for (int i = availablePeriods.IndexOf(startPeriod); i < availablePeriods.Count; i++) { ITimePeriod gap = availablePeriods[i]; TimeSpan gapRemining = gap.End - seekMoment; bool isTargetPeriod = false; switch (seekBoundaryMode) { case SeekBoundaryMode.Fill: isTargetPeriod = gapRemining >= remaining; break; case SeekBoundaryMode.Next: isTargetPeriod = gapRemining > remaining; break; } if (isTargetPeriod) { DateTime end = seekMoment + remaining.Value; remaining = null; return(end); } remaining = remaining - gapRemining; if (i == availablePeriods.Count - 1) { return(null); } seekMoment = availablePeriods[i + 1].Start; // next period } break; case SeekDirection.Backward: for (int i = availablePeriods.IndexOf(startPeriod); i >= 0; i--) { ITimePeriod gap = availablePeriods[i]; TimeSpan gapRemining = seekMoment - gap.Start; bool isTargetPeriod = false; switch (seekBoundaryMode) { case SeekBoundaryMode.Fill: isTargetPeriod = gapRemining >= remaining; break; case SeekBoundaryMode.Next: isTargetPeriod = gapRemining > remaining; break; } if (isTargetPeriod) { DateTime end = seekMoment - remaining.Value; remaining = null; return(end); } remaining = remaining - gapRemining; if (i == 0) { return(null); } seekMoment = availablePeriods[i - 1].End; // previous period } break; } return(null); } // CalculateEnd