NCrontab.Advanced.CrontabSchedule.InternalGetNextOccurence C# (CSharp) Method

InternalGetNextOccurence() private method

private InternalGetNextOccurence ( System.DateTime baseValue, System.DateTime endValue ) : System.DateTime
baseValue System.DateTime
endValue System.DateTime
return System.DateTime
        private DateTime InternalGetNextOccurence(DateTime baseValue, DateTime endValue)
        {
            var newValue = baseValue;
            var overflow = true;

            var isSecondFormat = Format == CronStringFormat.WithSeconds || Format == CronStringFormat.WithSecondsAndYears;
            var isYearFormat = Format == CronStringFormat.WithYears || Format == CronStringFormat.WithSecondsAndYears;

            // First things first - trim off any time components we don't need
            newValue = newValue.AddMilliseconds(-newValue.Millisecond);
            if (!isSecondFormat) newValue = newValue.AddSeconds(-newValue.Second);

            var minuteFilters = Filters[CrontabFieldKind.Minute].Where(x => x is ITimeFilter).Cast<ITimeFilter>().ToList();
            var hourFilters = Filters[CrontabFieldKind.Hour].Where(x => x is ITimeFilter).Cast<ITimeFilter>().ToList();

            var firstSecondValue = newValue.Second;
            var firstMinuteValue = minuteFilters.Select(x => x.First()).Min();
            var firstHourValue = hourFilters.Select(x => x.First()).Min();

            var newSeconds = newValue.Second;
            if (isSecondFormat)
            {
                var secondFilters = Filters[CrontabFieldKind.Second].Where(x => x is ITimeFilter).Cast<ITimeFilter>().ToList();
                firstSecondValue = secondFilters.Select(x => x.First()).Min();
                newSeconds = Increment(secondFilters, newValue.Second, firstSecondValue, out overflow);
                newValue = new DateTime(newValue.Year, newValue.Month, newValue.Day, newValue.Hour, newValue.Minute, newSeconds);
                if (!overflow && !IsMatch(newValue))
                {
                    newSeconds = firstSecondValue;
                    newValue = new DateTime(newValue.Year, newValue.Month, newValue.Day, newValue.Hour, newValue.Minute, newSeconds);
                    overflow = true;
                }
                if (!overflow) return MinDate(newValue, endValue);
            }

            var newMinutes = Increment(minuteFilters, newValue.Minute + (overflow ? 0 : -1), firstMinuteValue, out overflow);
            newValue = new DateTime(newValue.Year, newValue.Month, newValue.Day, newValue.Hour, newMinutes, overflow ? firstSecondValue : newSeconds);
            if (!overflow && !IsMatch(newValue))
            {
                newSeconds = firstSecondValue;
                newMinutes = firstMinuteValue;
                newValue = new DateTime(newValue.Year, newValue.Month, newValue.Day, newValue.Hour, newMinutes, firstSecondValue);
                overflow = true;
            }
            if (!overflow) return MinDate(newValue, endValue);

            var newHours = Increment(hourFilters, newValue.Hour + (overflow ? 0 : -1), firstHourValue, out overflow);
            newValue = new DateTime(newValue.Year, newValue.Month, newValue.Day, newHours,
                overflow ? firstMinuteValue : newMinutes,
                overflow ? firstSecondValue : newSeconds);

            if (!overflow && !IsMatch(newValue))
            {
                newValue = new DateTime(newValue.Year, newValue.Month, newValue.Day, firstHourValue, firstMinuteValue, firstSecondValue);
                overflow = true;
            }

            if (!overflow) return MinDate(newValue, endValue);

            List<ITimeFilter> yearFilters = null;
            if (isYearFormat) yearFilters = Filters[CrontabFieldKind.Year].Where(x => x is ITimeFilter).Cast<ITimeFilter>().ToList();

            // Sooo, this is where things get more complicated.
            // Since the filtering of days relies on what month/year you're in
            // (for weekday/nth day filters), we'll only increment the day, and
            // check all day/month/year filters.  Might be a litle slow, but we
            // won't miss any days that way.

            // Also, if we increment to the next day, we need to set the hour, minute and second
            // fields to their "first" values, since that would be the earliest they'd run.  We
            // only have to do this after the initial AddDays call.  FYI - they're already at their
            // first values if overflowHour = True.  :-)

            // This feels so dirty.  This is to catch the odd case where you specify
            // 12/31/9999 23:59:59.999 as your end date, and you don't have any matches,
            // so it reaches the max value of DateTime and throws an exception.
            try { newValue = newValue.AddDays(1); } catch { return endValue; }

            while (!(IsMatch(newValue, CrontabFieldKind.Day) && IsMatch(newValue, CrontabFieldKind.DayOfWeek) && IsMatch(newValue, CrontabFieldKind.Month) && (!isYearFormat || IsMatch(newValue, CrontabFieldKind.Year))))
            {
                if (newValue >= endValue) return MinDate(newValue, endValue);

                // In instances where the year is filtered, this will speed up the path to get to endValue
                // (without having to actually go to endValue)
                if (isYearFormat && yearFilters.Select(x => x.Next(newValue.Year - 1)).All(x => x == null)) return endValue;

                // Ugh...have to do the try/catch again...
                try { newValue = newValue.AddDays(1); } catch { return endValue; }
            }

            return MinDate(newValue, endValue);
        }