/// <summary> /// <paramref name="start"/> 시각으로부터 <paramref name="offset"/> 기간을 뺀 (즉 이전의) 시각을 계산합니다. /// </summary> /// <param name="start">시작 시각</param> /// <param name="offset">기간(Duration)</param> /// <param name="seekBoundaryMode">검색시 경계에 대한 모드</param> /// <returns></returns> public virtual DateTime?Subtract(DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next) { if (IsDebugEnabled) { log.Debug("Start 시각[{0}] + Duration[{1}]의 시각을 계산합니다.... SeekBoundaryMode=[{2}]", start, offset, seekBoundaryMode); } if (IncludePeriods.Count == 0 && ExcludePeriods.Count == 0) { return(start.Subtract(offset)); } TimeSpan?remaining; var end = offset < TimeSpan.Zero ? CalculateEnd(start, offset.Negate(), SeekDirection.Forward, seekBoundaryMode, out remaining) : CalculateEnd(start, offset, SeekDirection.Backward, seekBoundaryMode, out remaining); if (IsDebugEnabled) { log.Debug("Start 시각[{0}] + Duration[{1}]의 시각 End=[{2}], remaining=[{3}] 입니다!!! SeekBoundaryMode=[{4}]", start, offset, end, remaining, seekBoundaryMode); } return(end); }
} // Subtract // ---------------------------------------------------------------------- public override DateTime?Add(DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next) { if (weekDays.Count == 0 && ExcludePeriods.Count == 0 && workingHours.Count == 0) { return(start.Add(offset)); } return(offset < TimeSpan.Zero ? CalculateEnd(start, offset.Negate(), SeekDirection.Backward, seekBoundaryMode) : CalculateEnd(start, offset, SeekDirection.Forward, seekBoundaryMode)); } // Add
// ---------------------------------------------------------------------- public virtual DateTime? Subtract( DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next ) { if ( includePeriods.Count == 0 && excludePeriods.Count == 0 ) { return start.Subtract( offset ); } TimeSpan? remaining; return offset < TimeSpan.Zero ? CalculateEnd( start, offset.Negate(), SeekDirection.Forward, seekBoundaryMode, out remaining ) : CalculateEnd( start, offset, SeekDirection.Backward, seekBoundaryMode, out remaining ); }
} // Subtract // ---------------------------------------------------------------------- public virtual DateTime?Add(DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next) { if (includePeriods.Count == 0 && excludePeriods.Count == 0) { return(start.Add(offset)); } TimeSpan?remaining; return(offset < TimeSpan.Zero ? CalculateEnd(start, offset.Negate(), SeekDirection.Backward, seekBoundaryMode, out remaining) : CalculateEnd(start, offset, SeekDirection.Forward, seekBoundaryMode, out remaining)); } // Add
protected DateTime?CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode) { if (offset < TimeSpan.Zero) { throw new InvalidOperationException("time span must be positive"); } DateTime?endDate = null; DateTime moment = start; TimeSpan?remaining = offset; Week week = new Week(start, Calendar); // search end date, iteraring week by week while (week != null) { base.IncludePeriods.Clear(); base.IncludePeriods.AddAll(GetAvailableWeekPeriods(week)); endDate = CalculateEnd(moment, remaining.Value, seekDirection, seekBoundaryMode, out remaining); if (endDate != null || !remaining.HasValue) { break; } switch (seekDirection) { case SeekDirection.Forward: week = FindNextWeek(week); if (week != null) { moment = week.Start; } break; case SeekDirection.Backward: week = FindPreviousWeek(week); if (week != null) { moment = week.End; } break; } } return(endDate); }
/// <summary> /// <paramref name="start"/> 시각으로부터 <paramref name="offset"/> 기간을 뺀 (즉 이전의) 시각을 계산합니다. /// </summary> /// <param name="start">시작 시각</param> /// <param name="offset">기간(Duration)</param> /// <param name="seekBoundaryMode">검색시 경계에 대한 모드</param> /// <returns></returns> public virtual DateTime? Subtract(DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next) { if(IsDebugEnabled) log.Debug("Start 시각[{0}] + Duration[{1}]의 시각을 계산합니다.... SeekBoundaryMode=[{2}]", start, offset, seekBoundaryMode); if(IncludePeriods.Count == 0 && ExcludePeriods.Count == 0) return start.Subtract(offset); TimeSpan? remaining; var end = offset < TimeSpan.Zero ? CalculateEnd(start, offset.Negate(), SeekDirection.Forward, seekBoundaryMode, out remaining) : CalculateEnd(start, offset, SeekDirection.Backward, seekBoundaryMode, out remaining); if(IsDebugEnabled) log.Debug("Start 시각[{0}] + Duration[{1}]의 시각 End=[{2}], remaining=[{3}] 입니다!!! SeekBoundaryMode=[{4}]", start, offset, end, remaining, seekBoundaryMode); return end; }
/// <summary> /// <paramref name="start"/> 시각으로부터 <paramref name="offset"/> 기간을 뺀 (즉 이전의) 시각을 계산합니다. /// </summary> /// <param name="start">시작 시각</param> /// <param name="offset">기간(Duration)</param> /// <param name="seekBoundaryMode">검색시 경계에 대한 모드</param> /// <returns></returns> public override DateTime?Subtract(DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next) { if (IsDebugEnabled) { log.Debug("Start 시각[{0}] - Duration[{1}]의 시각을 계산합니다.... SeekBoundaryMode=[{2}]", start, offset, seekBoundaryMode); } if (WeekDays.Count == 0 && ExcludePeriods.Count == 0 && WorkingHours.Count == 0) { return(start.Subtract(offset)); } var end = offset < TimeSpan.Zero ? CalculateEnd(start, offset.Negate(), SeekDirection.Forward, seekBoundaryMode) : CalculateEnd(start, offset, SeekDirection.Backward, seekBoundaryMode); if (IsDebugEnabled) { log.Debug("Start 시각[{0}] - Duration[{1}]의 시각 End=[{2}] 입니다!!! SeekBoundaryMode=[{3}]", start, offset, end, seekBoundaryMode); } return(end); }
// ---------------------------------------------------------------------- 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; }
/// <summary> /// <paramref name="start"/>시각으로부터 <paramref name="offset"/> 만큼 떨어진 시각을 구합니다. /// </summary> /// <param name="start">기준 시각</param> /// <param name="offset">기간</param> /// <param name="seekDirection">검색 방향 (이전|이후)</param> /// <param name="seekBoundaryMode">검색 값 포함 여부</param> /// <param name="remaining">짜투리 기간</param> /// <returns>기준 시각으로터 오프셋만큼 떨어진 시각</returns> protected DateTime?CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode, out TimeSpan?remaining) { if (IsDebugEnabled) { log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구합니다... " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}]", start, offset, seekDirection, seekBoundaryMode); } Guard.Assert(offset >= TimeSpan.Zero, "offset 값은 항상 0 이상이어야 합니다. offset=[{0}]", offset); remaining = offset; // search periods ITimePeriodCollection searchPeriods = new TimePeriodCollection(IncludePeriods); if (searchPeriods.Count == 0) { searchPeriods.Add(TimeRange.Anytime); } // available periods ITimePeriodCollection availablePeriods = new TimePeriodCollection(); if (ExcludePeriods.Count == 0) { availablePeriods.AddAll(searchPeriods); } else { // 예외 기간을 제외합니다. // var gapCalculator = new TimeGapCalculator <TimeRange>(); var query = searchPeriods #if !SILVERLIGHT .AsParallel() .AsOrdered() #endif .SelectMany(searchPeriod => ExcludePeriods.HasOverlapPeriods(searchPeriod) ? gapCalculator.GetGaps(ExcludePeriods, searchPeriod) // 예외 기간과 검색 기간에서 겹치지 않는 기간을 계산하여, 추려냅니다. : new TimePeriodCollection { searchPeriod } // 예외 기간과 겹쳐진 부분이 없다면, 기간 전체를 추가합니다. ); availablePeriods.AddAll(query); } // 유효한 Period가 없다면 중단합니다. if (availablePeriods.Count == 0) { return(null); } // 기간중에 중복되는 부분의 없도록 유효한 기간을 결합합니다. if (availablePeriods.Count > 1) { var periodCombiner = new TimePeriodCombiner <TimeRange>(); availablePeriods = periodCombiner.CombinePeriods(availablePeriods); } // 첫 시작 기간을 찾습니다. // DateTime seekMoment; var startPeriod = (seekDirection == SeekDirection.Forward) ? FindNextPeriod(start, availablePeriods, out seekMoment) : FindPreviousPeriod(start, availablePeriods, out seekMoment); // 첫 시작 기간이 없다면 중단합니다. if (startPeriod == null) { return(null); } // 오프셋 값이 0 이라면, 바로 다음 값이므로 seekMoment를 반환합니다. if (offset == TimeSpan.Zero) { return(seekMoment); } if (seekDirection == SeekDirection.Forward) { for (var i = availablePeriods.IndexOf(startPeriod); i < availablePeriods.Count; i++) { var gap = availablePeriods[i]; var gapRemaining = gap.End - seekMoment; if (IsDebugEnabled) { log.Debug("Seek Forward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); } var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if (isTargetPeriod) { var end = seekMoment + remaining.Value; remaining = null; return(end); } remaining = remaining - gapRemaining; if (i == availablePeriods.Count - 1) { return(null); } seekMoment = availablePeriods[i + 1].Start; // next period } } else { for (var i = availablePeriods.IndexOf(startPeriod); i >= 0; i--) { var gap = availablePeriods[i]; var gapRemaining = seekMoment - gap.Start; if (IsDebugEnabled) { log.Debug("Seek Backward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); } var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if (isTargetPeriod) { var end = seekMoment - remaining.Value; remaining = null; return(end); } remaining = remaining - gapRemaining; if (i == 0) { return(null); } seekMoment = availablePeriods[i - 1].End; // previous period } } 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
/// <summary> /// <paramref name="start"/>시각으로부터 <paramref name="offset"/> 만큼 떨어진 시각을 구합니다. /// </summary> /// <param name="start">기준 시각</param> /// <param name="offset">기간</param> /// <param name="seekDirection">검색 방향 (이전|이후)</param> /// <param name="seekBoundaryMode">검색 값 포함 여부</param> /// <param name="remaining">짜투리 기간</param> /// <returns>기준 시각으로터 오프셋만큼 떨어진 시각</returns> protected DateTime? CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode, out TimeSpan? remaining) { if(IsDebugEnabled) log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구합니다... " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}]", start, offset, seekDirection, seekBoundaryMode); Guard.Assert(offset >= TimeSpan.Zero, "offset 값은 항상 0 이상이어야 합니다. offset=[{0}]", offset); remaining = offset; // search periods ITimePeriodCollection searchPeriods = new TimePeriodCollection(IncludePeriods); if(searchPeriods.Count == 0) searchPeriods.Add(TimeRange.Anytime); // available periods ITimePeriodCollection availablePeriods = new TimePeriodCollection(); if(ExcludePeriods.Count == 0) { availablePeriods.AddAll(searchPeriods); } else { // 예외 기간을 제외합니다. // var gapCalculator = new TimeGapCalculator<TimeRange>(); var query = searchPeriods #if !SILVERLIGHT .AsParallel() .AsOrdered() #endif .SelectMany(searchPeriod => ExcludePeriods.HasOverlapPeriods(searchPeriod) ? gapCalculator.GetGaps(ExcludePeriods, searchPeriod) // 예외 기간과 검색 기간에서 겹치지 않는 기간을 계산하여, 추려냅니다. : new TimePeriodCollection { searchPeriod } // 예외 기간과 겹쳐진 부분이 없다면, 기간 전체를 추가합니다. ); availablePeriods.AddAll(query); } // 유효한 Period가 없다면 중단합니다. if(availablePeriods.Count == 0) return null; // 기간중에 중복되는 부분의 없도록 유효한 기간을 결합합니다. if(availablePeriods.Count > 1) { var periodCombiner = new TimePeriodCombiner<TimeRange>(); availablePeriods = periodCombiner.CombinePeriods(availablePeriods); } // 첫 시작 기간을 찾습니다. // DateTime seekMoment; var startPeriod = (seekDirection == SeekDirection.Forward) ? FindNextPeriod(start, availablePeriods, out seekMoment) : FindPreviousPeriod(start, availablePeriods, out seekMoment); // 첫 시작 기간이 없다면 중단합니다. if(startPeriod == null) return null; // 오프셋 값이 0 이라면, 바로 다음 값이므로 seekMoment를 반환합니다. if(offset == TimeSpan.Zero) return seekMoment; if(seekDirection == SeekDirection.Forward) { for(var i = availablePeriods.IndexOf(startPeriod); i < availablePeriods.Count; i++) { var gap = availablePeriods[i]; var gapRemaining = gap.End - seekMoment; if(IsDebugEnabled) log.Debug("Seek Forward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if(isTargetPeriod) { var end = seekMoment + remaining.Value; remaining = null; return end; } remaining = remaining - gapRemaining; if(i == availablePeriods.Count - 1) return null; seekMoment = availablePeriods[i + 1].Start; // next period } } else { for(var i = availablePeriods.IndexOf(startPeriod); i >= 0; i--) { var gap = availablePeriods[i]; var gapRemaining = seekMoment - gap.Start; if(IsDebugEnabled) log.Debug("Seek Backward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if(isTargetPeriod) { var end = seekMoment - remaining.Value; remaining = null; return end; } remaining = remaining - gapRemaining; if(i == 0) return null; seekMoment = availablePeriods[i - 1].End; // previous period } } return null; }
// ---------------------------------------------------------------------- public override DateTime? Add( DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next ) { if ( weekDays.Count == 0 && ExcludePeriods.Count == 0 && workingHours.Count == 0 ) { return start.Add( offset ); } return offset < TimeSpan.Zero ? CalculateEnd( start, offset.Negate(), SeekDirection.Backward, seekBoundaryMode ) : CalculateEnd( start, offset, SeekDirection.Forward, seekBoundaryMode ); }
// ---------------------------------------------------------------------- protected DateTime? CalculateEnd( DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode ) { if ( offset < TimeSpan.Zero ) { throw new InvalidOperationException( "time span must be positive" ); } DateTime? endDate = null; DateTime moment = start; TimeSpan? remaining = offset; Week week = new Week( start, calendar ); // search end date, iteraring week by week while ( week != null ) { base.IncludePeriods.Clear(); base.IncludePeriods.AddAll( GetAvailableWeekPeriods( week ) ); endDate = CalculateEnd( moment, remaining.Value, seekDirection, seekBoundaryMode, out remaining ); if ( endDate != null || !remaining.HasValue ) { break; } switch ( seekDirection ) { case SeekDirection.Forward: week = FindNextWeek( week ); if ( week != null ) { moment = week.Start; } break; case SeekDirection.Backward: week = FindPreviousWeek( week ); if ( week != null ) { moment = week.End; } break; } } return endDate; }
/// <summary> /// <paramref name="start"/> 시각으로부터 <paramref name="offset"/> 기간이 지난 시각을 계산합니다. /// </summary> /// <param name="start">시작 시각</param> /// <param name="offset">기간(Duration)</param> /// <param name="seekBoundaryMode">검색시 경계에 대한 모드</param> /// <returns></returns> public override DateTime? Add(DateTime start, TimeSpan offset, SeekBoundaryMode seekBoundaryMode = SeekBoundaryMode.Next) { if(IsDebugEnabled) log.Debug("Start 시각[{0}] + Duration[{1}]의 시각을 계산합니다.... SeekBoundaryMode=[{2}]", start, offset, seekBoundaryMode); if(WeekDays.Count == 0 && ExcludePeriods.Count == 0 && WorkingHours.Count == 0) return start.Add(offset); var end = offset < TimeSpan.Zero ? CalculateEnd(start, offset.Negate(), SeekDirection.Backward, seekBoundaryMode) : CalculateEnd(start, offset, SeekDirection.Forward, seekBoundaryMode); if(IsDebugEnabled) log.Debug("Start 시각[{0}] - Duration[{1}]의 시각 End=[{2}] 입니다!!! SeekBoundaryMode=[{3}]", start, offset, end, seekBoundaryMode); return end; }
/// <summary> /// <paramref name="start"/>시각으로부터 <paramref name="offset"/> 만큼 떨어진 시각을 구합니다. /// </summary> /// <param name="start">기준 시각</param> /// <param name="offset">기간</param> /// <param name="seekDirection">검색 방향 (이전|이후)</param> /// <param name="seekBoundaryMode">검색 값 포함 여부</param> /// <returns>기준 시각으로터 오프셋만큼 떨어진 시각</returns> protected DateTime? CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode) { if(IsDebugEnabled) log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구합니다... " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}], Calendar=[{4}]", start, offset, seekDirection, seekBoundaryMode, TimeCalendar); Guard.Assert(offset >= TimeSpan.Zero, "offset 값은 TimeSpan.Zero 이상이어야 합니다. offset=[{0}]", offset); DateTime? end = null; DateTime moment = start; TimeSpan? remaining = offset; var week = new WeekRange(start, TimeCalendar); while(week != null) { base.IncludePeriods.Clear(); base.IncludePeriods.AddAll(GetAvailableWeekPeriods(week)); if(IsDebugEnabled) log.Debug("가능한 기간은=[{0}]", base.IncludePeriods.CollectionToString()); end = CalculateEnd(moment, remaining.Value, seekDirection, seekBoundaryMode, out remaining); if(end != null || remaining.HasValue == false) break; if(seekDirection == SeekDirection.Forward) { week = FindNextWeek(week); if(week != null) moment = week.Start; } else { week = FindPreviousWeek(week); if(week != null) moment = week.End; } } if(IsDebugEnabled) log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구했습니다!!! " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}], Calendar=[{4}], end=[{5}], remaining=[{6}]", start, offset, seekDirection, seekBoundaryMode, TimeCalendar, end, remaining); return end; }
/// <summary> /// <paramref name="start"/>시각으로부터 <paramref name="offset"/> 만큼 떨어진 시각을 구합니다. /// </summary> /// <param name="start">기준 시각</param> /// <param name="offset">기간</param> /// <param name="seekDirection">검색 방향 (이전|이후)</param> /// <param name="seekBoundaryMode">검색 값 포함 여부</param> /// <returns>기준 시각으로터 오프셋만큼 떨어진 시각</returns> protected DateTime?CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode) { if (IsDebugEnabled) { log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구합니다... " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}], Calendar=[{4}]", start, offset, seekDirection, seekBoundaryMode, TimeCalendar); } Guard.Assert(offset >= TimeSpan.Zero, "offset 값은 TimeSpan.Zero 이상이어야 합니다. offset=[{0}]", offset); DateTime?end = null; DateTime moment = start; TimeSpan?remaining = offset; var week = new WeekRange(start, TimeCalendar); while (week != null) { base.IncludePeriods.Clear(); base.IncludePeriods.AddAll(GetAvailableWeekPeriods(week)); if (IsDebugEnabled) { log.Debug("가능한 기간은=[{0}]", base.IncludePeriods.CollectionToString()); } end = CalculateEnd(moment, remaining.Value, seekDirection, seekBoundaryMode, out remaining); if (end != null || remaining.HasValue == false) { break; } if (seekDirection == SeekDirection.Forward) { week = FindNextWeek(week); if (week != null) { moment = week.Start; } } else { week = FindPreviousWeek(week); if (week != null) { moment = week.End; } } } if (IsDebugEnabled) { log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구했습니다!!! " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}], Calendar=[{4}], end=[{5}], remaining=[{6}]", start, offset, seekDirection, seekBoundaryMode, TimeCalendar, end, remaining); } return(end); }