Skip to content

Fix TimeZoneInfo.ConvertTime producing wrong results near DateTime.MinValue/MaxValue#127009

Merged
tarekgh merged 5 commits intodotnet:mainfrom
tarekgh:fix/timezone-converttime-minvalue
Apr 16, 2026
Merged

Fix TimeZoneInfo.ConvertTime producing wrong results near DateTime.MinValue/MaxValue#127009
tarekgh merged 5 commits intodotnet:mainfrom
tarekgh:fix/timezone-converttime-minvalue

Conversation

@tarekgh
Copy link
Copy Markdown
Member

@tarekgh tarekgh commented Apr 16, 2026

Fix

TimeZoneInfo.ConvertTime returns incorrect results when converting a DateTime near DateTime.MinValue between positive-offset time zones (or near DateTime.MaxValue between negative-offset time zones).

Root Cause

ConvertTime works by first converting the input to UTC, then from UTC to the destination time zone. When the intermediate UTC value has negative ticks (outside DateTime range), SafeCreateDateTimeFromTicks clamps it to DateTime.MinValue (ticks = 0). The destination offset is then added to this clamped value, producing a wrong result.

Example: Converting 0001-01-01 01:13:15 from UTC+4 to UTC+3:

  • Intermediate UTC ticks = 1h13m15s - 4h = negative (before epoch)
  • Clamped to DateTime.MinValue (ticks = 0)
  • Add destination offset (+3h) → 0001-01-01 03:00:00
  • Expected: 0001-01-01 00:13:15

Fix

Changed TryLocalToUtc to return raw long utcTicks instead of a clamped DateTime. ConvertTime now computes the final result from raw ticks (utcTicks + destOffset.Ticks), only creating 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.

Testing

  • Added 7 new [Theory] test cases using custom time zones (cross-platform):
    • 4 cases for near-DateTime.MinValue with positive offsets (including exact repro from issue)
    • 3 cases for near-DateTime.MaxValue with negative offsets (symmetric)
  • All 68,715+ System.Runtime.Tests pass with zero failures.
  • Local benchmarking shows no performance regression.

Fixes #126940

…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
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-datetime
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

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

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 TryLocalToUtc to return raw utcTicks (long) instead of a clamped DateTime.
  • Updated ConvertTime to compute final local ticks from utcTicks + destOffset.Ticks, using a clamped UTC DateTime only 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.

Comment thread src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs Outdated
@tarekgh tarekgh added this to the 11.0.0 milestone Apr 16, 2026
@tarekgh tarekgh requested a review from jozkee April 16, 2026 16:06
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 16, 2026 16:11
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 16, 2026 16:39
Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@tarekgh tarekgh merged commit f9892f6 into dotnet:main Apr 16, 2026
148 of 154 checks passed
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.

TimeZoneInfo.ConvertTime - different results for .NET 10 and .NET 11

3 participants