Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a function at a certain time with C# Reactive Extensions

Is it possible to call a function at a certain time, using Reactive Extensions?

For example, if I want to call method foo() at exactly 9am and 1pm, everyday, I could use the Timer class to check every few seconds if it's 9am or 1pm, or even the Observable.Interval function. But is there a more efficient way to do this? So I'm not checking every few seconds if it's time to call foo() yet, but rather an observable that will call foo() on its own at the right time.

like image 444
Drake Avatar asked Dec 13 '14 23:12

Drake


People also ask

How to use time() in C?

time() function in CThe time() function is defined in time. h (ctime in C++) header file. This function returns the time since 00:00:00 UTC, January 1, 1970 (Unix timestamp) in seconds. If second is not a null pointer, the returned value is also stored in the object pointed to by second.

How to get time taken in C?

To get the elapsed time, we can get the time using clock() at the beginning, and at the end of the tasks, then subtract the values to get the differences. After that, we will divide the difference by CLOCK_PER_SEC (Number of clock ticks per second) to get the processor time.

How do you call a function after some time in C++?

We can use a delay() function for this purpose in our code. This function is imported from the “dos. h” header file in C++. We can run the code after a specific time in C++ using delay() function.

Can you call a function in a function in C?

No you can't have a nested function in C . The closest you can come is to declare a function inside the definition of another function. The definition of that function has to appear outside of any other function body, though.


1 Answers

Just use the overload of Timer that accepts 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, to the best of its ability, that your notification will occur on the exact date and time specified, even with regard to timer drift and if the system clock changes before the timer duration has elapsed.

Here's an extension method that generalizes the problem further.

Usage:

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 by defining the input as an infinite sequence from an iterator block, per Jeroen Mostert's answer, then you can use Generate as follows.

Observable.Generate(
  GetScheduleTimes().GetEnumerator(), 
  e => e.MoveNext(), 
  e => e, 
  e => e.Current, 
  e => e.Current);

Jeroen Mostert's answer (deleted) provided an example implementation of GetScheduleTimes, but basically it was just an iterator block that yielded an infinite sequence of DateTimeOffset values in a while loop, with each loop incrementing the values' day by 1.

like image 58
Dave Sexton Avatar answered Sep 27 '22 18:09

Dave Sexton