Scheduler built with observables
public static IObservable&lt;DateTime&gt; Create(TimeSpan interval, IDateTime dateTime)
{
    return
        Observable
            .Interval(TimeSpan.FromSeconds(1))
            .Select(_ =&gt; dateTime.Now());
}



You forgot to plug interval parameter. And, I would consider renaming it resolution.

I wouldn't trust the values from selecting DatetTime::Now or DateTime::UtcNow:

// try running this for a while
var scheduler = Scheduler.Create(TimeSpan.FromSeconds(1), new LocalDateTime());
scheduler.Schedule("0/1 * * * * * *", schedule =&gt;
{
    Console.WriteLine($"ACTION: {schedule}: {schedule:ss.fff}");
});



ACTION: 2018-04-13 6:28:29 PM: 29.987
ACTION: 2018-04-13 6:28:30 PM: 30.987
ACTION: 2018-04-13 6:28:32 PM: 32.001
ACTION: 2018-04-13 6:28:33 PM: 33.001



Depending on CronExpression::Contains implementation, the scheduler could be skipping task when DateTime.Now.Millisecond gets near 0 or 999.

I've attempted to solve that by adding index * interval to a snapshot of time, however this solution suffered a problem: The delay is cumulative, so the schedule's time will slowly deviate(fall behind) from the real time:

// dont use this
public static IObservable&lt;DateTime&gt; Create2(TimeSpan interval, IDateTime dateTime)
{
    var snapshot = dateTime.Now();
    var offset = interval.Ticks - snapshot.Ticks % interval.Ticks;

    return Observable.Interval(interval)
        .Delay(TimeSpan.FromTicks(offset))
        .Select(x =&gt; snapshot.AddTicks(interval.Ticks * (x + 1) + offset));
}


An better solution would be to provide a range of time, and check if expression fall between the range:

public static IDisposable Schedule2(this IObservable&lt;DateTime&gt; schedules, string cronExpressionString, Action&lt;DateTime&gt; action)
{
    var cronExpression = Regex.Match(cronExpressionString, @"^0/(?&lt;s&gt;\d+)");
    return
        schedules
            .Scan(default(TimeRange), (previous, x) =&gt; new TimeRange(previous?.End, x))
            .Where(cronExpression.Contains)
            .Subscribe(action);
}

public class TimeRange
{
    public DateTime? Start { get; }
    public DateTime End { get; }

    public TimeRange(DateTime? start, DateTime end)
    {
        this.Start = start;
        this.End = end;
    }
}