Reputation: 10045
I have a Job scheduling program, and I am changing it into being more precise with its timing.
It should run on a given x minute interval, based on a starting time.
An easy but inefficient solution to my problem would be:
var scheduledOn = new DateTime(2017, 01, 3, 9, 5, 0);
var minutesBetweenJob = 5;
while (scheduledOn < DateTime.Now)
{
scheduledOn = scheduledOn + TimeSpan.FromMinutes(minutesBetweenJob);
}
Console.WriteLine("Correct Answer: " + scheduledOn);
But if the jobs hasn't run for, lets say a year. It would need to run ALOT of 5 minutes iterations before it gets it right, which is very inefficient!
I have tried a few ways, but they ended up being very inaccurate, because of floating point errors.
Upvotes: 0
Views: 859
Reputation: 10045
This was the solution to my own problem. It seems to perform atleast 10-20 times faster than my previous attempt, and is precise down to the milissecond.
I figured out that my floating point errors would come when dealing with nanoseconds, so trimming the datetime to seconds or even milisseconds, solved the issue.
/// <summary>
/// This function calculates the next datetime interval for a given datetime, based on a scheduled time.
/// Eg. Job is scheduled to run at 15:00 and every 5 minutes, the next scheduled time will be 15.05, then 15.10 and so forth.
/// The calculated time will always be after the "afterDateTime".
/// </summary>
/// <param name="baseDatetime">The time that scheduled time is based on</param>
/// <param name="interval">The interval in minutes</param>
/// <param name="afterDateTime">Usually datetime now, but the date it should be "after".</param>
/// <param name="tickPrecision">[Optional (Default = TimeSpan.TicksPerSecond)] Determine the tick precision, the lowest possible value is TicksPerMillisecond</param>
/// <returns>The next scheduled time</returns>
public static DateTime CalculateNextScheduledTime(DateTime baseDatetime, int interval, DateTime afterDateTime, long tickPrecision = TimeSpan.TicksPerSecond)
{
// Reset afterDateTime to tick precision.
afterDateTime = new DateTime(((long)afterDateTime.Ticks / tickPrecision) * tickPrecision);
// (Subtract Difference in modulus time intervals between aftertime base time) + the interval.
return afterDateTime - TimeSpan.FromMinutes((afterDateTime - baseDatetime).TotalMinutes % interval) + TimeSpan.FromMinutes(interval);
}
Upvotes: 3