69 lines
2.3 KiB
C#
69 lines
2.3 KiB
C#
namespace JSMR.Infrastructure.Common.Time;
|
|
|
|
public interface ITimeProvider
|
|
{
|
|
DateTimeOffset Now();
|
|
DateTimeOffset Local(int year, int month, int day, int hour);
|
|
DateTimeOffset Local(DateTimeOffset offset);
|
|
}
|
|
|
|
public abstract class TimeProvider : ITimeProvider
|
|
{
|
|
protected abstract string Id { get; }
|
|
protected abstract string[] TimeZoneIds { get; }
|
|
|
|
private readonly IClock _clock;
|
|
private readonly TimeZoneInfo _timeZone;
|
|
|
|
public TimeProvider(IClock clock)
|
|
{
|
|
_clock = clock;
|
|
_timeZone = ResolveTimeZone();
|
|
}
|
|
|
|
private TimeZoneInfo ResolveTimeZone()
|
|
{
|
|
foreach (string timeZoneId in TimeZoneIds)
|
|
{
|
|
if (TimeZoneInfo.TryFindSystemTimeZoneById(timeZoneId, out TimeZoneInfo? timeZoneInfo))
|
|
return timeZoneInfo;
|
|
}
|
|
|
|
throw new TimeZoneNotFoundException($"Unable to resolve time zone for: {Id} ({string.Join(" / ", TimeZoneIds)})");
|
|
}
|
|
|
|
public DateTimeOffset Now() => TimeZoneInfo.ConvertTime(_clock.UtcNow, _timeZone);
|
|
public DateTimeOffset Local(DateTimeOffset offset) => TimeZoneInfo.ConvertTime(offset, _timeZone);
|
|
|
|
public DateTimeOffset Local(int year, int month, int day, int hour)
|
|
{
|
|
DateTime local = new(year, month, day, hour, 0, 0, DateTimeKind.Unspecified);
|
|
TimeSpan offset = _timeZone.GetUtcOffset(local);
|
|
|
|
return new DateTimeOffset(local, offset);
|
|
}
|
|
|
|
public DateTimeOffset CurrentScanAnchor()
|
|
{
|
|
DateTimeOffset now = Now();
|
|
DateTimeOffset midnight = Local(now.Year, now.Month, now.Day, 0);
|
|
DateTimeOffset fourPm = Local(now.Year, now.Month, now.Day, 16);
|
|
|
|
return now >= fourPm ? fourPm : midnight;
|
|
}
|
|
|
|
public DateTimeOffset PreviousScanAnchor(DateTimeOffset scanAnchorTokyo)
|
|
{
|
|
// Normalize to Tokyo (no-op if already)
|
|
var a = TimeZoneInfo.ConvertTime(scanAnchorTokyo, _timeZone);
|
|
return a.Hour == 16
|
|
? Local(a.Year, a.Month, a.Day, 0)
|
|
: Local(a.AddDays(-1).Year, a.AddDays(-1).Month, a.AddDays(-1).Day, 16);
|
|
}
|
|
}
|
|
|
|
public class TokyoTimeProvider(IClock clock) : TimeProvider(clock)
|
|
{
|
|
protected override string Id => "Tokyo Standard Time";
|
|
protected override string[] TimeZoneIds => ["Tokyo Standard Time", "Asia/Tokyo"];
|
|
} |