Example #1
0
        /// <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
Example #3
0
        // ----------------------------------------------------------------------
        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 );
        }
Example #4
0
        }         // 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
Example #5
0
        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);
        }
Example #6
0
        /// <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;
        }
Example #7
0
        /// <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);
        }
Example #8
0
        // ----------------------------------------------------------------------
        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;
        }
Example #9
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);
        }
Example #10
0
        }         // 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
Example #11
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;
        }
Example #12
0
        // ----------------------------------------------------------------------
        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 );
        }
Example #13
0
        // ----------------------------------------------------------------------
        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;
        }
Example #14
0
        /// <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;
        }
Example #15
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>
        /// <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;
        }
Example #16
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>
        /// <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);
        }