Just use the Timer overload, which takes a DateTimeOffset value. You can use Defer and Repeat to create an "absolute interval".
Observable.Defer(() => DateTime.Now.Hour < 9 ? Observable.Timer(DateTime.Today.AddHours(9)) : DateTime.Now.Hour < 13 ? Observable.Timer(DateTime.Today.AddHours(13)) : Observable.Timer(DateTime.Today.AddDays(1).AddHours(9))) .Repeat() .Subscribe(...);
Rx automatically ensures , as far as possible, your notification will occur at the exact date and time specified even with respect to the timer and if the system clock changes before the timer expires.
An extension method is used here that further generalizes the problem.
Using:
Observable2.Daily(TimeSpan.FromHours(9), TimeSpan.FromHours(13)).Subscribe(...);
Definition:
public static partial class Observable2 { public static IObservable<long> Daily(params TimeSpan[] times) { Contract.Requires(times != null); Contract.Requires(Contract.ForAll(times, time => time > TimeSpan.Zero)); Contract.Requires(Contract.ForAll(times, time => time.TotalDays < 1)); return Daily(Scheduler.Default, times); } public static IObservable<long> Daily(IScheduler scheduler, params TimeSpan[] times) { Contract.Requires(times != null); Contract.Requires(Contract.ForAll(times, time => time > TimeSpan.Zero)); Contract.Requires(Contract.ForAll(times, time => time.TotalDays < 1)); if (times.Length == 0) return Observable.Never<long>(); // Do not sort in place. var sortedTimes = times.ToList(); sortedTimes.Sort(); return Observable.Defer(() => { var now = DateTime.Now; var next = sortedTimes.FirstOrDefault(time => now.TimeOfDay < time); var date = next > TimeSpan.Zero ? now.Date.Add(next) : now.Date.AddDays(1).Add(sortedTimes[0]); Debug.WriteLine("Next @" + date + " from " + sortedTimes.Aggregate("", (s, t) => s + t + ", ")); return Observable.Timer(date, scheduler); }) .Repeat() .Scan(-1L, (n, _) => n + 1); } }
Update:
If you want to be more “functional” in your approach, specifying input as an infinite sequence from an iterator block to Jeroen Mostert’s answer, you can use Generate as follows.
Observable.Generate( GetScheduleTimes().GetEnumerator(), e => e.MoveNext(), e => e, e => e.Current, e => e.Current);
Jeroen Mostert's (remote) answer provided an example implementation of GetScheduleTimes , but basically it was just an iterator block that gave an infinite sequence of DateTimeOffset values in a while loop, with each loop increasing the value of day 1.