Skip to content

Simplify TryParseFormatO fractional-seconds handling#129006

Closed
danmoseley wants to merge 1 commit into
dotnet:mainfrom
danmoseley:simplify-parseo-fraction
Closed

Simplify TryParseFormatO fractional-seconds handling#129006
danmoseley wants to merge 1 commit into
dotnet:mainfrom
danmoseley:simplify-parseo-fraction

Conversation

@danmoseley
Copy link
Copy Markdown
Member

The "O" (ISO 8601 round-trip) format always contains exactly 7 fractional-second digits, matching DateTime's tick precision (TimeSpan.TicksPerSecond == 10_000_000). The integer value of those seven digits is therefore already the sub-second tick count.

Today's code in TryParseFormatO:

double fraction = (f1*1e6 + ... + f7) / 10000000.0;
// ...
dateTime.TryAddTicks((long)Math.Round(fraction * TimeSpan.TicksPerSecond), out result.parsedDate);

/ 1e7 then Math.Round(... * 1e7) is identity for every value the parser will ever see. Each 7-digit fraction is <= 9_999_999 < 2^24, exactly representable as double; 1e7 is exactly representable; the two correctly-rounded ops bound absolute error well under 0.5; Math.Round then recovers the original integer. (The round-trip must already be exact today, since DateTime.ToString("O") writes the same 7 digits — otherwise format-then-parse would drift.)

So this just keeps the value as int fractionTicks and passes it straight to TryAddTicks. Bit-exact equivalent, smaller, more obvious.

Risk

None. Existing "O"-format round-trip tests in DateTimeTests continue to pass.

Perf

Small win on Perf_DateTime.ParseO micro-benchmark, but the absolute magnitude is hard to pin down precisely against R2R'd CoreLib because tiny IL changes can shift crossgen code layout: a short-job A/B showed ParseO at ~9% but the unmodified ParseR control also drifted ~8%, indicating layout noise dominates at this measurement length. The change is justified as a simplification; perf is a side benefit.

The "O" (ISO 8601 round-trip) format always contains exactly 7
fractional-second digits, matching DateTime's tick precision
(TimeSpan.TicksPerSecond == 10_000_000). The integer value of those
seven digits is therefore already the sub-second tick count.

The existing code computed `n / 1e7` then `Math.Round(... * 1e7)` to get
back to ticks -- a no-op for every value the parser will ever see. Each
7-digit fraction is <= 9_999_999 < 2^24, exactly representable as
double; 1e7 is exactly representable; the two correctly-rounded ops
bound the absolute error well under 0.5; Math.Round then recovers the
original integer. (The round-trip must already be exact today, since
DateTime.ToString("O") writes the same 7 digits -- otherwise
format-then-parse would drift.)

Keep the value as int and pass it straight to TryAddTicks. Bit-exact
equivalent, smaller, more obvious.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@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.

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