using System.Globalization; namespace MeterVault.Core.Tests.Analysis; /// Frozen instants and zones for the period tests — nothing here reads the wall clock. internal static class AnalysisClock { public static readonly TimeZoneInfo Berlin = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin"); public static readonly TimeZoneInfo NewYork = TimeZoneInfo.FindSystemTimeZoneById("America/New_York"); /// An unambiguous local wall-clock time in , as an instant. public static DateTimeOffset At(TimeZoneInfo zone, int year, int month, int day, int hour = 0, int minute = 0) { var wall = new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Unspecified); Assert.False(zone.IsInvalidTime(wall), $"{wall:s} does not exist in {zone.Id}"); Assert.False(zone.IsAmbiguousTime(wall), $"{wall:s} is ambiguous in {zone.Id}; give the offset"); return new DateTimeOffset(wall, zone.GetUtcOffset(wall)); } /// A local wall-clock time with an explicit offset — for the hour an autumn fold repeats. public static DateTimeOffset At(int year, int month, int day, int hour, int minute, int offsetHours) => new(year, month, day, hour, minute, 0, TimeSpan.FromHours(offsetHours)); public static DateTimeOffset Utc(int year, int month, int day, int hour = 0, int minute = 0) => new(year, month, day, hour, minute, 0, TimeSpan.Zero); public static DateOnly Day(int year, int month, int day) => new(year, month, day); public static DateOnly Iso(string date) => DateOnly.ParseExact(date, "yyyy-MM-dd", CultureInfo.InvariantCulture); public static DateTimeOffset IsoInstant(string instant) => DateTimeOffset.ParseExact(instant, "yyyy-MM-dd'T'HH:mm'Z'", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); /// The wall clock an instant shows in , for readable assertions. public static string Wall(DateTimeOffset instant, TimeZoneInfo zone) => TimeZoneInfo.ConvertTime(instant, zone).ToString("yyyy-MM-dd HH:mm zzz", CultureInfo.InvariantCulture); }