Ejemplo n.º 1
0
        /// <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);
        }
Ejemplo n.º 2
0
        /// <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;
        }