The problem here (as Shlomo mentioned) is that we need to open the window in t2 BEFORE the t1 event occurs. Unfortunately this isn't possible because once we reach the event in t1 we're already past the point we need to open the window in t2.
What we can do instead is to shift t2 forwards in time using Delay(). If we offset it by x (the before time) we can reframe the question as "get the events in t2 that occur in the window opening at t1 and closing at t1 + x + y. We can use a GroupJoin to solve that.
var scheduler = new HistoricalScheduler();
var t1 = Observable.Interval(TimeSpan.FromMilliseconds(200), scheduler)
.Select(l => (char)('A' + l));
var t2 = Observable.Interval(TimeSpan.FromMilliseconds(100), scheduler);
var x = TimeSpan.FromMilliseconds(100); //before time
var y = TimeSpan.FromMilliseconds(100); //after time
var delayedT2 = t2.Delay(x, scheduler);
var g = t1.GroupJoin(delayedT2 ,
_ => Observable.Timer(x + y, scheduler),
_ => Observable.Empty<Unit>(scheduler),
(a, b) => new { a, b}
);
scheduler.Start();
This gives the result:
{ A, [1,2] }
{ B, [3,4] }
{ C, [5,6] }
This result still isn't quite what you were expecting. This because in your example t2 events are occurring at exactly the same instant t1 events. In this case the t1 + y event is processed first and closes the window before the t2 event can be included. This means we are effectively getting (t1-01:00) <= t1 < (t1 + 01:00). E.g. The window for A is 01:0000 - 02.9999... which is why 3 occurring at 03:00 is not included.
This can be fixed to be inclusive by simply adding a single tick to our y time
var y = TimeSpan.FromMilliseconds(100).Add(TimeSpan.FromTicks(1));