using System; using PolyTraderSharp.Services; using Xunit; namespace PolyTrader.Tests { /// /// Sicherheitsnetz für die Anzeige-Zeitzone. Die frühere Fassung rechnete hart gegen die /// WINDOWS-ID "W. Europe Standard Time" – auf Linux ohne vollständiges ICU wirft das. /// Diese Tests halten fest, dass beide Schreibweisen tragen und ein unbekannter Wert /// niemals fatal ist, sondern auf die Systemzeitzone zurückfällt. /// public class AppTimeZoneTests { [Theory] [InlineData("Europe/Berlin")] // IANA – Empfehlung, funktioniert auf beiden Plattformen [InlineData("W. Europe Standard Time")] // Windows – Altbestand muss weiter tragen public void Known_ids_resolve_to_the_same_zone(string id) { AppTimeZone.Configure(id); // Der Bezeichner unterscheidet sich je Plattform; entscheidend ist der Versatz. var berlinWinter = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Utc); Assert.Equal(TimeSpan.FromHours(1), AppTimeZone.Current.GetUtcOffset(berlinWinter)); var berlinSommer = new DateTime(2026, 7, 15, 12, 0, 0, DateTimeKind.Utc); Assert.Equal(TimeSpan.FromHours(2), AppTimeZone.Current.GetUtcOffset(berlinSommer)); } [Fact] public void Unknown_id_falls_back_to_system_zone_and_warns() { string? warning = null; AppTimeZone.Configure("Gibt/EsNicht", w => warning = w); Assert.Equal(TimeZoneInfo.Local.Id, AppTimeZone.Current.Id); Assert.NotNull(warning); Assert.Contains("Gibt/EsNicht", warning); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void Empty_id_means_system_zone(string? id) { AppTimeZone.Configure(id); Assert.Equal(TimeZoneInfo.Local.Id, AppTimeZone.Current.Id); } [Fact] public void ToDisplay_converts_utc_into_the_configured_zone() { AppTimeZone.Configure("Europe/Berlin"); // 12:00 UTC im Januar = 13:00 Berliner Zeit (MEZ). var utc = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Utc); Assert.Equal(13, AppTimeZone.ToDisplay(utc).Hour); } [Fact] public void ToDisplay_leaves_already_local_timestamps_alone() { AppTimeZone.Configure(null); // Systemzeitzone var local = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Local); Assert.Equal(12, AppTimeZone.ToDisplay(local).Hour); } // ── §8 des Linux-Befundberichts: Logs stempelten UTC, die Oberfläche zeigte Ortszeit ── [Fact] public void Now_carries_the_offset_of_the_display_zone() { AppTimeZone.Configure("Pacific/Auckland"); try { var now = AppTimeZone.Now; var expected = AppTimeZone.Current.GetUtcOffset(DateTimeOffset.UtcNow); // Der Versatz gehört zur ANZEIGE-Zone. Ein umgerechneter DateTime trüge ihn nicht // mit sich – beim Formatieren mit "zzz" käme dann der Versatz des Systems heraus. Assert.Equal(expected, now.Offset); Assert.Equal(DateTime.UtcNow.Add(expected).Date, AppTimeZone.Today); } finally { AppTimeZone.Configure(null); } } [Fact] public void Log_entries_are_stamped_in_the_display_zone() { AppTimeZone.Configure("Europe/Berlin"); try { var e = new LogMessageEventArgs("egal", LogLevel.Info); Assert.Equal(AppTimeZone.Current.GetUtcOffset(e.Timestamp.UtcDateTime), e.Timestamp.Offset); // Und die abgelegte JSONL-Zeile trägt denselben Versatz mit. var parsed = DateTimeOffset.Parse( LogJson.ParseLine(LogJson.Format(e))!.Time, System.Globalization.CultureInfo.InvariantCulture); Assert.Equal(e.Timestamp.Offset, parsed.Offset); Assert.Equal(e.Timestamp.ToUnixTimeSeconds(), parsed.ToUnixTimeSeconds()); } finally { AppTimeZone.Configure(null); } } } }