using MeterVault.Core.Normalization;
namespace MeterVault.Infrastructure.Options;
///
/// The instance timezone (, SDD §10) as every part of the
/// application uses it: the normalizer divides months in it, readers bucket in it and turn requested
/// dates into instants with it. One resolution, so none of them disagrees about where a month starts.
///
public static class InstanceTimeZone
{
/// The configured zone, or UTC when is empty or not a known zone.
public static TimeZoneInfo Resolve(string? id)
{
if (string.IsNullOrWhiteSpace(id))
{
return TimeZoneInfo.Utc;
}
return TimeZoneInfo.TryFindSystemTimeZoneById(id, out var zone) ? zone : TimeZoneInfo.Utc;
}
///
/// The IANA id for a configured zone. .NET also accepts Windows ids ("W. Europe Standard Time"), but
/// PostgreSQL buckets by IANA names only, so a Windows id is translated rather than passed to SQL.
/// Anything unrecognised is returned unchanged, to be reported at startup.
///
public static string Canonical(string id)
{
if (!TimeZoneInfo.TryFindSystemTimeZoneById(id, out var zone) || zone.HasIanaId)
{
return id;
}
return TimeZoneInfo.TryConvertWindowsIdToIanaId(zone.Id, out var iana) ? iana : id;
}
/// The instant a local date starts — the lower bound a range of that date begins at.
public static DateTimeOffset StartOf(DateOnly date, TimeZoneInfo zone) => GapAttribution.LocalMidnight(date, zone);
}