public void IndexOfTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay( now ); TimePeriodCollection timePeriods = new TimePeriodCollection(); Assert.AreEqual( timePeriods.IndexOf( new TimeRange() ), -1 ); Assert.AreEqual( timePeriods.IndexOf( new TimeBlock() ), -1 ); timePeriods.AddAll( schoolDay ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Lesson1 ), 0 ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Break1 ), 1 ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Lesson2 ), 2 ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Break2 ), 3 ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Lesson3 ), 4 ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Break3 ), 5 ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Lesson4 ), 6 ); timePeriods.Remove( schoolDay.Lesson1 ); Assert.AreEqual( timePeriods.IndexOf( schoolDay.Lesson1 ), -1 ); }
// ---------------------------------------------------------------------- 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; }
} // 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