Fix TimeZoneInfo.ConvertTime producing wrong results near DateTime.MinValue/MaxValue#127009
Merged
tarekgh merged 5 commits intodotnet:mainfrom Apr 16, 2026
Merged
Conversation
…nValue/MaxValue When converting between time zones where the intermediate UTC value falls outside the DateTime representable range (e.g., converting 0001-01-01 01:13:15 from UTC+4 to UTC+3), the intermediate UTC ticks were clamped to DateTime.MinValue by SafeCreateDateTimeFromTicks. The destination offset was then added to this clamped value, producing an incorrect result. The fix changes TryLocalToUtc to return raw ticks (long) instead of a clamped DateTime. ConvertTime now computes the final result from these raw ticks, only using a clamped DateTime for transition-table offset lookups. This avoids precision loss from double-clamping when the intermediate UTC value is outside DateTime range but the final result is valid. Fixes dotnet#126940
Contributor
|
Tagging subscribers to this area: @dotnet/area-system-datetime |
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes TimeZoneInfo.ConvertTime incorrect results when converting DateTime values near DateTime.MinValue/DateTime.MaxValue by avoiding clamping of the intermediate UTC value before applying the destination offset.
Changes:
- Refactored
TryLocalToUtcto return rawutcTicks(long) instead of a clampedDateTime. - Updated
ConvertTimeto compute final local ticks fromutcTicks + destOffset.Ticks, using a clamped UTCDateTimeonly for offset lookup. - Added new cross-platform regression tests using custom fixed-offset time zones for Min/Max boundary scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs | Computes conversion result using raw UTC ticks to avoid incorrect double-clamping near DateTime bounds. |
| src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Cache.cs | Changes TryLocalToUtc to output raw UTC ticks (can be outside DateTime range). |
| src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeZoneInfoTests.cs | Adds regression coverage for DateTime conversions near Min/Max values using custom offset-only zones. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
jozkee
approved these changes
Apr 16, 2026
This was referenced Apr 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix
TimeZoneInfo.ConvertTimereturns incorrect results when converting aDateTimenearDateTime.MinValuebetween positive-offset time zones (or nearDateTime.MaxValuebetween negative-offset time zones).Root Cause
ConvertTimeworks by first converting the input to UTC, then from UTC to the destination time zone. When the intermediate UTC value has negative ticks (outsideDateTimerange),SafeCreateDateTimeFromTicksclamps it toDateTime.MinValue(ticks = 0). The destination offset is then added to this clamped value, producing a wrong result.Example: Converting
0001-01-01 01:13:15from UTC+4 to UTC+3:1h13m15s - 4h= negative (before epoch)DateTime.MinValue(ticks = 0)0001-01-01 03:00:00❌0001-01-01 00:13:15✅Fix
Changed
TryLocalToUtcto return rawlong utcTicksinstead of a clampedDateTime.ConvertTimenow computes the final result from raw ticks (utcTicks + destOffset.Ticks), only creating a clampedDateTimefor transition-table offset lookups. This avoids precision loss from double-clamping when the intermediate UTC value is outsideDateTimerange but the final result is valid.Testing
[Theory]test cases using custom time zones (cross-platform):DateTime.MinValuewith positive offsets (including exact repro from issue)DateTime.MaxValuewith negative offsets (symmetric)System.Runtime.Testspass with zero failures.Fixes #126940