using FluentAssertions; using IBKRTrader.Modules.Supervisor.Services; namespace IBKRTrader.Tests.Modules.Supervisor; /// /// Der Tagesbericht soll um eine feste ORTSZEIT laufen – unabhängig davon, in welcher Zeitzone der /// Rechner steht. Vorher rechnete er gegen DateTimeKind.Local: eine US-Instanz auf einem /// UTC-Container hätte den „18-Uhr-Bericht" um 13 Uhr Ortszeit erzeugt, über eine Zeitgrenze /// hinweg, die im Bericht selbst nicht sichtbar ist. /// [Trait("cat", "unit")] public class DailyReportScheduleTests { private static readonly TimeZoneInfo Berlin = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin"); private static readonly TimeZoneInfo NewYork = TimeZoneInfo.FindSystemTimeZoneById("America/New_York"); [Fact] public void NaechsterLauf_LiegtHeute_WennDieStundeNochBevorsteht() { // 08:00 UTC = 10:00 Berliner Sommerzeit. 18 Uhr Berlin steht heute noch bevor = 16:00 UTC. var now = new DateTime(2026, 7, 15, 8, 0, 0, DateTimeKind.Utc); DailyReportService.NextRunUtc(now, 18, Berlin) .Should().Be(new DateTime(2026, 7, 15, 16, 0, 0, DateTimeKind.Utc)); } [Fact] public void NaechsterLauf_RutschtAufMorgen_WennDieStundeVorbeiIst() { // 20:00 UTC = 22:00 Berlin. 18 Uhr ist durch, also morgen 18 Uhr Berlin = 16:00 UTC. var now = new DateTime(2026, 7, 15, 20, 0, 0, DateTimeKind.Utc); DailyReportService.NextRunUtc(now, 18, Berlin) .Should().Be(new DateTime(2026, 7, 16, 16, 0, 0, DateTimeKind.Utc)); } [Fact] public void DieselbeStunde_ErgibtJeZoneEinenAnderenUtcZeitpunkt() { var now = new DateTime(2026, 7, 15, 6, 0, 0, DateTimeKind.Utc); // 18 Uhr Ortszeit: Berlin (UTC+2) = 16:00 UTC, New York (UTC-4) = 22:00 UTC. DailyReportService.NextRunUtc(now, 18, Berlin).Hour.Should().Be(16); DailyReportService.NextRunUtc(now, 18, NewYork).Hour.Should().Be(22); } [Fact] public void Winterzeit_NutztDenRichtigenVersatz() { // Januar: Berlin ist UTC+1, 18 Uhr Ortszeit = 17:00 UTC. var now = new DateTime(2026, 1, 15, 8, 0, 0, DateTimeKind.Utc); DailyReportService.NextRunUtc(now, 18, Berlin) .Should().Be(new DateTime(2026, 1, 15, 17, 0, 0, DateTimeKind.Utc)); } [Fact] public void Zeitumstellung_LaesstDenBerichtNichtAusfallen() { // 29.03.2026: Berlin stellt um 02:00 auf 03:00 vor – die Stunde 02:00 existiert nicht. // Der Bericht muss trotzdem laufen (auf die naechste gueltige Stunde ausweichen), // statt den Tag zu ueberspringen. var now = new DateTime(2026, 3, 28, 12, 0, 0, DateTimeKind.Utc); var next = DailyReportService.NextRunUtc(now, 2, Berlin); next.Should().BeAfter(now); next.Should().BeBefore(now.AddDays(2), "der Bericht darf hoechstens einen Tag spaeter kommen"); } [Fact] public void Ergebnis_IstImmerUtc() { var now = new DateTime(2026, 7, 15, 8, 0, 0, DateTimeKind.Utc); DailyReportService.NextRunUtc(now, 18, Berlin).Kind.Should().Be(DateTimeKind.Utc); } }