/// <summary>
        /// This method should not be used by the Quartz client.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Called by the scheduler at the time a <see cref="ITrigger" /> is first
        /// added to the scheduler, in order to have the <see cref="ITrigger" />
        /// compute its first fire time, based on any associated calendar.
        /// </para>
        ///
        /// <para>
        /// After this method has been called, <see cref="ITrigger.GetNextFireTimeUtc" />
        /// should return a valid answer.
        /// </para>
        /// </remarks>
        /// <returns>
        /// The first time at which the <see cref="ITrigger" /> will be fired
        /// by the scheduler, which is also the same value <see cref="ITrigger.GetNextFireTimeUtc" />
        /// will return (until after the first firing of the <see cref="ITrigger" />).
        /// </returns>
        public override DateTimeOffset?ComputeFirstFireTimeUtc(ICalendar calendar)
        {
            DateTimeOffset startTime          = TimeZoneUtil.ConvertTime(StartTimeUtc, TimeZone);
            DateTimeOffset?startTimeOfDayDate = StartTimeOfDay.GetTimeOfDayForDate(startTime);

            // If startTime is after the timeOfDay, then use starTime
            if (startTime > startTimeOfDayDate)
            {
                nextFireTimeUtc = GetFireTimeAfter(startTime);
            }
            else
            {
                nextFireTimeUtc = AdvanceToNextDayOfWeek(startTimeOfDayDate.Value, false);
            }

            // Check calendar for date-time exclusion
            while (nextFireTimeUtc != null && calendar != null &&
                   !calendar.IsTimeIncluded(nextFireTimeUtc.Value))
            {
                nextFireTimeUtc = GetFireTimeAfter(nextFireTimeUtc);

                if (nextFireTimeUtc == null)
                {
                    break;
                }

                //avoid infinite loop
                if (nextFireTimeUtc.Value.Year > YearToGiveupSchedulingAt)
                {
                    return(null);
                }
            }

            return(nextFireTimeUtc);
        }
        /// <summary>
        /// Returns the next time at which the <see cref="IDailyTimeIntervalTrigger" /> will
        /// fire, after the given time. If the trigger will not fire after the given
        /// time, <see langword="null" /> will be returned.
        /// </summary>
        /// <param name="afterTime"></param>
        /// <returns></returns>
        public override DateTimeOffset?GetFireTimeAfter(DateTimeOffset?afterTime)
        {
            // Check if trigger has completed or not.
            if (complete)
            {
                return(null);
            }

            // Check repeatCount limit
            if (repeatCount != RepeatIndefinitely && timesTriggered > repeatCount)
            {
                return(null);
            }

            // a. Increment afterTime by a second, so that we are comparing against a time after it!
            if (afterTime == null)
            {
                afterTime = SystemTime.UtcNow().AddSeconds(1);
            }
            else
            {
                afterTime = afterTime.Value.AddSeconds(1);
            }

            // if afterTime is before startTime, then return startTime directly.
            if (afterTime <= startTimeUtc)
            {
                return(startTimeUtc);
            }

            // now change to local time zone
            afterTime = TimeZoneUtil.ConvertTime(afterTime.Value, TimeZone);

            // b.Check to see if afterTime is after endTimeOfDay or not.
            // If yes, then we need to advance to next day as well.
            bool afterTimePassEndTimeOfDay = false;

            if (endTimeOfDay != null)
            {
                afterTimePassEndTimeOfDay = afterTime.Value > endTimeOfDay.GetTimeOfDayForDate(afterTime).Value;
            }
            DateTimeOffset?fireTime = AdvanceToNextDayOfWeek(afterTime.Value, afterTimePassEndTimeOfDay);

            if (fireTime == null)
            {
                return(null);
            }

            // apply timezone for this date & time
            fireTime = new DateTimeOffset(fireTime.Value.DateTime, TimeZone.GetUtcOffset(fireTime.Value));

            // c. Calculate and save fireTimeEndDate variable for later use
            DateTimeOffset fireTimeEndDate;

            if (endTimeOfDay == null)
            {
                fireTimeEndDate = new TimeOfDay(23, 59, 59).GetTimeOfDayForDate(fireTime).Value;
            }
            else
            {
                fireTimeEndDate = endTimeOfDay.GetTimeOfDayForDate(fireTime).Value;
            }

            // apply the proper offset for the end date
            fireTimeEndDate = new DateTimeOffset(fireTimeEndDate.DateTime, this.TimeZone.GetUtcOffset(fireTimeEndDate.DateTime));

            // e. Check fireTime against startTime or startTimeOfDay to see which go first.
            DateTimeOffset fireTimeStartDate = startTimeOfDay.GetTimeOfDayForDate(fireTime).Value;

            // apply the proper offset for the start date
            fireTimeStartDate = new DateTimeOffset(fireTimeStartDate.DateTime, this.TimeZone.GetUtcOffset(fireTimeStartDate.DateTime));

            if (fireTime < startTimeUtc && startTimeUtc < fireTimeStartDate)
            {
                return(fireTimeStartDate);
            }
            if (fireTime < startTimeUtc && startTimeUtc > fireTimeStartDate)
            {
                return(startTimeUtc);
            }
            if (fireTime > startTimeUtc && fireTime < fireTimeStartDate)
            {
                return(fireTimeStartDate);
            }

            // Always adjust the startTime to be startTimeOfDay
            startTimeUtc = fireTimeStartDate.ToUniversalTime();

            // f. Continue to calculate the fireTime by incremental unit of intervals.
            startTimeUtc = TimeZoneUtil.ConvertTime(startTimeUtc, TimeZone);
            long secondsAfterStart = (long)(fireTime.Value - startTimeUtc).TotalSeconds;
            long repeatLong        = RepeatInterval;

            DateTimeOffset sTime      = startTimeUtc;
            IntervalUnit   repeatUnit = RepeatIntervalUnit;

            if (repeatUnit == IntervalUnit.Second)
            {
                long jumpCount = secondsAfterStart / repeatLong;
                if (secondsAfterStart % repeatLong != 0)
                {
                    jumpCount++;
                }

                sTime    = sTime.AddSeconds(RepeatInterval * (int)jumpCount);
                fireTime = sTime;
            }
            else if (repeatUnit == IntervalUnit.Minute)
            {
                long jumpCount = secondsAfterStart / (repeatLong * 60L);
                if (secondsAfterStart % (repeatLong * 60L) != 0)
                {
                    jumpCount++;
                }
                sTime    = sTime.AddMinutes(RepeatInterval * (int)jumpCount);
                fireTime = sTime;
            }
            else if (repeatUnit == IntervalUnit.Hour)
            {
                long jumpCount = secondsAfterStart / (repeatLong * 60L * 60L);
                if (secondsAfterStart % (repeatLong * 60L * 60L) != 0)
                {
                    jumpCount++;
                }
                sTime    = sTime.AddHours(RepeatInterval * (int)jumpCount);
                fireTime = sTime;
            }

            // g. Ensure this new fireTime is within one day, or else we need to advance to next day.
            if (fireTime > fireTimeEndDate)
            {
                // Check to see if fireTime has pass fireTime's end of day. If not, we need to advance by one day.
                DateTimeOffset fireTimeEndOfDay = new TimeOfDay(23, 59, 59).GetTimeOfDayForDate(fireTimeEndDate).Value;
                if (fireTime > fireTimeEndOfDay)
                {
                    fireTime = AdvanceToNextDayOfWeek(fireTime.Value, false);
                }
                else
                {
                    fireTime = AdvanceToNextDayOfWeek(fireTime.Value, true);
                }
                if (fireTime == null)
                {
                    return(null);
                }

                // Check to see if next day fireTime is before startTimeOfDay, if not, we need to set to startTimeOfDay.
                DateTimeOffset nextDayfireTimeStartDate = StartTimeOfDay.GetTimeOfDayForDate(fireTime).Value;
                if (fireTime < nextDayfireTimeStartDate)
                {
                    fireTime = nextDayfireTimeStartDate;
                }
            }

            // i. Return calculated fireTime.
            return(fireTime.Value.ToUniversalTime());
        }