A simple question: how do I execute a command every minute?

This sounds like a trivial question, using a Timer of course.

However, here is a caveat: whenever the timer starts, we want the command to happen exactly at the beginning of a new minute.

This means, if the application starts at 10:30:05, we want the command to execute at 10:31:00, NOT 10:31:05. The next command should execute at 10:32:00.

Therefore, we cannot really just start a Timer with interval of 1 minute, because that does not really meet the requirement.

In this case, Observable.Generate comes to rescue.

 

DateTime previousExecute = DateTime.now; // initial time
var end = previousExecution.AddMinutes(3); // this is just a dummy end

	var obs = Observable
		.Generate(
			previousExecution, // initialState
			x => x < end, //condition 
			x =>
			{
				var diff = GetSecondsToNextMinute(x);
				var y = x.AddSeconds(diff);
				return y;
			}, //iterate
			x =>
			{
				previousExecution = x;
				return previousExecution;
			}, //resultSelector
			x => TimeSpan.FromSeconds(GetSecondsToNextMinute()) //timeSelector
		, TaskPoolScheduler.Default.DisableOptimizations(typeof(ISchedulerLongRunning)));

obs.Subscribe(i =>
{
    i.Dump("Triggered");
});

double GetSecondsToNextMinute(DateTime curr)
{
    var diff = (60 - curr.Second);
    return diff;
}

The above code will calculate the difference between the previous execution time and the next minute. The scheduler of Observable will tick after the selected time passes until the stopping condition is met.

Why is it useful?

This is interesting because we can now create a scheduler in code to execute a command exactly at midnight.

Here is the complete code:

        public static IObservable DynamicDaily(DateTime startTime, IScheduler scheduler = null)
        {
            double d = GetSecondsToNextDay(startTime);
            Log.Debug("Daily Observable starts at: {StartTime} and waiting for {Tick} seconds from next day", d, startTime);

            DateTime lastExecutionDateTime;
            IScheduler sc = scheduler ?? TaskPoolScheduler.Default.DisableOptimizations(typeof(ISchedulerLongRunning));

            var obs = Observable
                .Generate(
                    startTime, // initialState
                    x => x < DateTime.MaxValue, //condition
                    x =>
                    {
                        double diff = GetSecondsToNextDay(x);
                        DateTime retval = x.AddSeconds(diff).Date;
                        return retval;
                    }, //iterate
                    x =>
                    {
                        lastExecutionDateTime = x;
                        return lastExecutionDateTime;
                    }, //resultSelector
                    x =>
                    {
                        double diff = GetSecondsToNextDay(x);
                        return TimeSpan.FromSeconds(diff);
                    }
                    //timeSelector
                    , sc);

            return obs;
        }

        private static double GetSecondsToNextDay(DateTime dateTime)
        {
            DateTime nextDay = dateTime.AddDays(1).Date;
            double diff = (nextDay - dateTime).TotalSeconds;
            return diff;
        }

We can use a TestScheduler to test the above function.