namespace ClaudeDo.Data.Models; /// /// The optional local-time window restricting when PowerUse applies /// ( / End, both HH:mm). /// Pure on purpose — the caller supplies "now", so the window logic is testable without a clock. /// public static class PowerUseWindow { /// Parses an HH:mm string; anything else (including null/blank) is null. public static TimeOnly? Parse(string? value) => TimeOnly.TryParseExact(value?.Trim(), "HH:mm", out var t) ? t : null; /// Round-trips a stored/typed value through , so an unparseable /// string is stored as null instead of sitting in the column pretending to be a window. public static string? Normalize(string? value) => Parse(value)?.ToString("HH:mm"); /// /// True when falls inside the window. Half-open: start is included, /// end is not. Either bound missing = no window at all = always true (the pre-window /// behaviour). A window whose end is before its start wraps over midnight. /// public static bool Contains(TimeOnly now, TimeOnly? start, TimeOnly? end) { if (start is not { } from || end is not { } to) return true; return from < to ? now >= from && now < to : now >= from || now < to; } /// Convenience overload for the two stored strings and a wall clock. public static bool IsOpen(DateTime now, string? start, string? end) => Contains(TimeOnly.FromDateTime(now), Parse(start), Parse(end)); }