Skip to content

Conversation

@cimryan
Copy link
Contributor

@cimryan cimryan commented Aug 11, 2025

The difference between DateTime.Now And DateTime.UtcNow should be the offset from the local time zone to UTC. Before this change, 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.
Utc was selected because local times often lead to tests that only work in a specific time zone and should be selected intentionally.

…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.
Copilot AI review requested due to automatic review settings August 11, 2025 22:09

This comment was marked as outdated.

…nspecified as if it had a Kind of Utc.

It was decided that Utc would be a less fragile default than Local.
@vbreuss vbreuss requested a review from Copilot August 12, 2025 19:16
Copy link

Copilot AI left a 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

Copy link
Member

@vbreuss vbreuss left a 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 👍

@vbreuss vbreuss enabled auto-merge (squash) August 13, 2025 12:54
@vbreuss vbreuss merged commit 7c6b256 into Testably:main Aug 13, 2025
12 checks passed
@github-actions
Copy link

This is addressed in release v4.3.2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants