in src/WebJobs.Extensions/Extensions/Timers/Scheduling/ScheduleMonitor.cs [81:161]
public virtual async Task<TimeSpan> CheckPastDueAsync(string timerName, DateTimeOffset now, TimerSchedule schedule, ScheduleStatus lastStatus)
{
DateTimeOffset recordedNextOccurrence;
if (lastStatus == null)
{
// If we've never recorded a status for this timer, write an initial
// status entry. This ensures that for a new timer, we've captured a
// status log for the next occurrence even though no occurrence has happened yet
// (ensuring we don't miss an occurrence)
DateTimeOffset nextOccurrence = schedule.GetNextOccurrence(now.LocalDateTime);
lastStatus = new ScheduleStatus
{
Last = DefaultDateTime,
Next = nextOccurrence.LocalDateTime,
LastUpdated = now.LocalDateTime
};
await UpdateStatusAsync(timerName, lastStatus);
recordedNextOccurrence = nextOccurrence;
}
else
{
DateTimeOffset expectedNextOccurrence;
// Track the time that was used to create 'expectedNextOccurrence'.
DateTimeOffset lastUpdated;
if (lastStatus.Last > DefaultDateTimeThreshold)
{
// If we have a 'Last' value, we know that we used this to calculate 'Next'
// in a previous invocation.
expectedNextOccurrence = schedule.GetNextOccurrence(lastStatus.Last);
lastUpdated = lastStatus.Last;
}
else if (lastStatus.LastUpdated > DefaultDateTimeThreshold)
{
// If the trigger has never fired, we won't have 'Last', but we will have
// 'LastUpdated', which tells us the last time that we used to calculate 'Next'.
expectedNextOccurrence = schedule.GetNextOccurrence(lastStatus.LastUpdated);
lastUpdated = lastStatus.LastUpdated;
}
else
{
// If we do not have 'LastUpdated' or 'Last', we don't have enough information to
// properly calculate 'Next', so we'll calculate it from the current time.
expectedNextOccurrence = schedule.GetNextOccurrence(now.LocalDateTime);
lastUpdated = now;
}
// ensure that the schedule hasn't been updated since the last
// time we checked, and if it has, update the status to use the new schedule
if (lastStatus.Next != expectedNextOccurrence)
{
// if the schedule has changed and the next occurrence is in the past,
// recalculate it based on the current time as we don't want it to register
// immediately as 'past due'.
if (now > expectedNextOccurrence)
{
expectedNextOccurrence = schedule.GetNextOccurrence(now.LocalDateTime);
lastUpdated = now;
}
lastStatus.Last = DefaultDateTime;
lastStatus.Next = expectedNextOccurrence.LocalDateTime;
lastStatus.LastUpdated = lastUpdated.LocalDateTime;
await UpdateStatusAsync(timerName, lastStatus);
}
recordedNextOccurrence = lastStatus.Next;
}
if (now > recordedNextOccurrence)
{
// if now is after the last next occurrence we recorded, we know we've missed
// at least one schedule instance and we are past due
return now - recordedNextOccurrence;
}
else
{
// not past due
return TimeSpan.Zero;
}
}