-
-
Notifications
You must be signed in to change notification settings - Fork 9
fix: MockTimeProvider constructor treats "now" having DateTimeKind.Unspecified as if it had DateTimeKind.Local #834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: MockTimeProvider constructor treats "now" having DateTimeKind.Unspecified as if it had DateTimeKind.Local #834
Conversation
…fied as if it had DateTimeKind.Local The difference between "DateTime.Now" And "DateTime.UtcNow" should be the offset from the local time zone to UTC. Before this change, the MockTimeSystem would produce a difference double that size when initialized with a DateTime having Kind DateTimeKind.Unspecified. The MockTimeSystem delegates to a MockDateTime wrapped around a TimeProviderMock. The TimeProviderMock would yield a DateTime with Kind DateTimeKind.Unspecified to the MockDateTime, which would then apply ToLocalTime() for "Now" or ToUniversalTime for "UtcNow". When applied to a DateTime with Kind DateTimeKind.Unspecified, ToLocalTime assumes that the value is in UTC, but ToUniversalTime assumes that it is Local. This discrepancy results in the doubling of the expected difference. Additionally, the test OnDateTimeRead_Today_ShouldExecuteCallbackWithCorrectParameter was failing, but only when run on systems outside of UTC. On systems running inside UTC, there was no difference between Now and UtcNow, so the error was hidden. So, when the MockTimeSystem is initialized with a DateTime which has Kind DateTimeKind.Unspecified, it should pick a specific Kind to use internally. Either Utc or Local would work. Local was selected for ergonomics: When a user of the MockTimeSystem specifies a literal DateTime without a Kind, e.g. "new Datime(2025, 8, 1, 9, 0, 0)", it seems more likely that they are intending to express a local time rather than a time in UTC.
Source/Testably.Abstractions.Testing/TimeSystem/TimeProviderMock.cs
Outdated
Show resolved
Hide resolved
…nspecified as if it had a Kind of Utc. It was decided that Utc would be a less fragile default than Local.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a bug in the MockTimeProvider where DateTimeKind.Unspecified values were causing incorrect time zone offset calculations. The issue resulted in double the expected difference between DateTime.Now and DateTime.UtcNow when initialized with unspecified DateTime values.
- Converts DateTimeKind.Unspecified to DateTimeKind.Utc in TimeProviderMock constructor
- Adds comprehensive test coverage for the time zone offset bug
- Updates existing tests to use proper DateTimeKind.Local values
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| Source/Testably.Abstractions.Testing/TimeSystem/TimeProviderMock.cs | Adds logic to convert DateTimeKind.Unspecified to DateTimeKind.Utc in constructor |
| Source/Testably.Abstractions.Testing/TimeProvider.cs | Documents the behavior for DateTimeKind.Unspecified parameters |
| Tests/Testably.Abstractions.Testing.Tests/MockTimeSystemTests.cs | Adds parameterized test for time zone offset behavior and fixes ToString test |
| Tests/Testably.Abstractions.Testing.Tests/TimeProviderTests.cs | Adds test for DateTimeKind.Unspecified conversion behavior |
| Tests/Testably.Abstractions.Testing.Tests/TimeSystem/NotificationHandlerTests.cs | Fixes test by using DateTimeKind.Local instead of unspecified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution, @cimryan. The change looks good to me 👍
|
This is addressed in release v4.3.2. |
The difference between
DateTime.NowAndDateTime.UtcNowshould be the offset from the local time zone to UTC. Before this change,MockTimeSystemwould produce a difference double that size when initialized with aDateTimehavingKindDateTimeKind.Unspecified.The
MockTimeSystemdelegates to aMockDateTimewrapped around aTimeProviderMock. TheTimeProviderMockwould yield aDateTimewithKindDateTimeKind.Unspecifiedto theMockDateTime, which would then applyToLocalTime()forNoworToUniversalTimeforUtcNow. When applied to aDateTimewithKindDateTimeKind.Unspecified,ToLocalTimeassumes that the value is in UTC, butToUniversalTimeassumes that it is Local. This discrepancy results in the doubling of the expected difference.Additionally, the test
OnDateTimeRead_Today_ShouldExecuteCallbackWithCorrectParameterwas failing, but only when run on systems outside of UTC. On systems running inside UTC, there was no difference betweenNowandUtcNow, so the error was hidden.So, when the
MockTimeSystemis initialized with aDateTimewhich hasKindDateTimeKind.Unspecified, it should pick a specific kind to use internally. EitherUtcorLocalwould work.Utcwas selected because local times often lead to tests that only work in a specific time zone and should be selected intentionally.