Problem
ITimeSystem.DateTime (IDateTime) only abstracts System.DateTime — it exposes Now, UtcNow, Today, MinValue, MaxValue, UnixEpoch. There is no seam for System.DateTimeOffset.
DateTimeOffset.Now / DateTimeOffset.UtcNow is a primary "current time" source in modern code (it is the recommended type for timestamps), so code that reads them directly cannot be made deterministic through this library today. The only workaround is new DateTimeOffset(timeSystem.DateTime.UtcNow), which does not reproduce the local-offset semantics of DateTimeOffset.Now.
Proposed API
Add an IDateTimeOffset abstraction and expose it on ITimeSystem:
namespace Testably.Abstractions.TimeSystem;
public interface IDateTimeOffset : ITimeSystemEntity
{
DateTimeOffset MaxValue { get; }
DateTimeOffset MinValue { get; }
DateTimeOffset UnixEpoch { get; }
DateTimeOffset Now { get; }
DateTimeOffset UtcNow { get; }
}
public interface ITimeSystem
{
IDateTime DateTime { get; }
IDateTimeOffset DateTimeOffset { get; } // new
// ...existing members
}
Notes / design
- The mock must be driven by the same
ITimeProvider clock that backs IDateTime, so SetTo / AdvanceBy / auto-advance move both consistently. Now should apply the local offset the same way DateTimeMock.Now uses ToLocalTime().
MinValue / MaxValue / UnixEpoch should track the corresponding overridable values already present on ITimeProvider (they are DateTime there; convert to DateTimeOffset).
- Adding a member to the public
ITimeSystem interface is a binary-breaking change for anyone who implements it directly (real + mock ship with the package, but third-party implementers would break). Consider whether this warrants a major version bump, or a default-interface-member on TFMs that support it.
- The
On notification handler (INotificationHandler for the time system) may want a matching DateTimeOffsetRead callback for parity with DateTimeRead.
Problem
ITimeSystem.DateTime(IDateTime) only abstractsSystem.DateTime— it exposesNow,UtcNow,Today,MinValue,MaxValue,UnixEpoch. There is no seam forSystem.DateTimeOffset.DateTimeOffset.Now/DateTimeOffset.UtcNowis a primary "current time" source in modern code (it is the recommended type for timestamps), so code that reads them directly cannot be made deterministic through this library today. The only workaround isnew DateTimeOffset(timeSystem.DateTime.UtcNow), which does not reproduce the local-offset semantics ofDateTimeOffset.Now.Proposed API
Add an
IDateTimeOffsetabstraction and expose it onITimeSystem:Notes / design
ITimeProviderclock that backsIDateTime, soSetTo/AdvanceBy/ auto-advance move both consistently.Nowshould apply the local offset the same wayDateTimeMock.NowusesToLocalTime().MinValue/MaxValue/UnixEpochshould track the corresponding overridable values already present onITimeProvider(they areDateTimethere; convert toDateTimeOffset).ITimeSysteminterface is a binary-breaking change for anyone who implements it directly (real + mock ship with the package, but third-party implementers would break). Consider whether this warrants a major version bump, or a default-interface-member on TFMs that support it.Onnotification handler (INotificationHandlerfor the time system) may want a matchingDateTimeOffsetReadcallback for parity withDateTimeRead.