Skip to content

Round-trip TimeZoneInfo.ToSerializedString on Unix (#19794)#131352

Draft
tarekgh wants to merge 1 commit into
dotnet:mainfrom
tarekgh:tarekgh/fix-19794-timezoneinfo-serialization-roundtrip
Draft

Round-trip TimeZoneInfo.ToSerializedString on Unix (#19794)#131352
tarekgh wants to merge 1 commit into
dotnet:mainfrom
tarekgh:tarekgh/fix-19794-timezoneinfo-serialization-roundtrip

Conversation

@tarekgh

@tarekgh tarekgh commented Jul 24, 2026

Copy link
Copy Markdown
Member

Fixes #19794

Problem

TimeZoneInfo.ToSerializedString / FromSerializedString did not round-trip on Unix. The custom string format only represents the Windows-shaped public projection of the adjustment rules (the output of GetAdjustmentRules()). Unix rules can carry information that projection cannot express, so the deserialized instance did not equal the original. Lossy cases include:

  • rules with NoDaylightTransitions set,
  • DateStart / DateEnd with DateTimeKind.Utc and sub-day boundaries,
  • sub-minute BaseUtcOffsetDelta,
  • multi-year internal rules that the public projection splits.

Fix

Append an optional full-fidelity copy of the internal adjustment rules after the separator that terminates the legacy rule list. This placement is backward compatible: older readers stop at that separator and never see the trailer, so previously serialized strings and Windows output are unchanged and remain valid.

  • A modern reader consumes the trailer and reconstructs the exact internal rules.
  • When the trailer is absent, or written with an unknown future version, the reader falls back to the legacy rules.
  • The trailer is only emitted when the legacy projection is actually lossy, so Windows-shaped time zones produce byte-identical output to before.

The legacy portion is also kept self-consistent for readers that ignore the trailer:

  • whole-day legacy boundaries are ordered so consecutive rules never overlap,
  • BaseUtcOffsetDelta is written in whole minutes, because the legacy reader rejects a fractional value.

The trailer count and rule ticks are untrusted input, so the rule count is bounded against the remaining input length, and out-of-range or overflowing tick values are translated into SerializationException.

Testing

Added and re-enabled tests in TimeZoneInfoTests:

  • re-enabled the Unix round-trip test over all system time zones,
  • Windows-shaped rules emit no trailer (byte-identical output),
  • full-fidelity round-trip including fixed and floating transitions and sub-minute BaseUtcOffsetDelta,
  • legacy rules remain valid and non-overlapping when the trailer is ignored,
  • malformed and overflowing trailers throw SerializationException,
  • an unknown trailer version is ignored.

Validated on Windows (618 tests) and Linux/WSL (2044 tests); all pass.

TimeZoneInfo.ToSerializedString/FromSerializedString did not round-trip on Unix
because the custom string format only represents the Windows-shaped public
projection of the adjustment rules. Unix rules can carry information the legacy
format cannot express (NoDaylightTransitions rules, UTC sub-day boundaries,
sub-minute BaseUtcOffsetDelta, or multi-year rules that get split), so the
deserialized instance did not match the original.

Append an optional full-fidelity copy of the internal adjustment rules after the
separator that terminates the legacy rule list. Older readers stop at that
separator and never see the trailer, so existing serialized strings and Windows
output are unchanged and remain valid. A modern reader consumes the trailer and
reconstructs the exact internal rules; when the trailer is absent or written with
an unknown future version, it falls back to the legacy rules.

Also make the legacy portion self-consistent when the trailer is ignored: order
the whole-day legacy boundaries so consecutive rules never overlap, and write
BaseUtcOffsetDelta in whole minutes since the legacy reader rejects a fractional
value. The trailer count and rule ticks are untrusted input, so bound the rule
count against the remaining input length and translate out-of-range or
overflowing tick values into SerializationException.
Copilot AI review requested due to automatic review settings July 24, 2026 22:45
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 updates TimeZoneInfo string serialization to support full-fidelity round-tripping of Unix time zone adjustment rules by appending an optional, versioned trailer after the legacy rule-list terminator, while keeping the legacy portion backward-compatible for older readers.

Changes:

  • Extend TimeZoneInfo.ToSerializedString to optionally emit a full-fidelity trailer of internal adjustment rules when the legacy (Windows-shaped) projection would be lossy.
  • Extend TimeZoneInfo.FromSerializedString to detect and consume the trailer (when present and recognized) and fall back to legacy rules otherwise.
  • Re-enable and expand TimeZoneInfoTests coverage for Unix round-tripping and trailer parsing/validation scenarios.

Reviewed changes

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

File Description
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.StringSerializer.cs Adds versioned full-fidelity trailer serialization/deserialization and keeps legacy output parseable for older runtimes.
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeZoneInfoTests.cs Re-enables Unix round-trip test and adds targeted tests for trailer emission, parsing, and malformed input handling.

@tarekgh

tarekgh commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

CC @Clockwork-Muse

@tarekgh
tarekgh requested a review from jozkee July 25, 2026 00:10
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.ToSerializedString/FromSerializedString do not round trip on Unix

2 participants