/// <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); }
/// <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; }