André Snede
André Snede

Reputation: 10045

Get next DateTime interval, after "now", based on a base DateTime

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

Answers (1)

Andr&#233; Snede
Andr&#233; Snede

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

Related Questions