Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Conversation

@stephentoub
Copy link
Member

Ports the code used by Utf8Parser, modified to support things Utf8Parser doesn't but DateTime{Offset{.ParseExact do, such as single-digit offset hours.

Local times (w/o timezone) improve by ~6x.
Times with timezones improve by ~2.5-2.75x.
All cases reduce allocations from 120 bytes across 5 objects down to 0.

Benchmark:

using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Running;

[MemoryDiagnoser]
[InProcess]
public class Benchmark
{
    private static void Main() => BenchmarkRunner.Run<Benchmark>();

    [Benchmark] public DateTime ParseExactLocal() => DateTime.ParseExact("2018-07-05T22:10:55.5595539", "o", null);
    [Benchmark] public DateTime ParseExactUtc() => DateTime.ParseExact("2018-07-05T22:10:55.5595539Z", "o", null);
    [Benchmark] public DateTime ParseExactOffset() => DateTime.ParseExact("2018-07-05T22:10:55.5595539+12:34", "o", null);
}

Before:

           Method |     Mean |    Error |   StdDev |   Median |  Gen 0 | Allocated |
----------------- |---------:|---------:|---------:|---------:|-------:|----------:|
  ParseExactLocal | 623.9 ns | 12.42 ns | 28.54 ns | 613.6 ns | 0.0277 |     120 B |
    ParseExactUtc | 962.3 ns | 32.57 ns | 96.03 ns | 932.3 ns | 0.0277 |     120 B |
 ParseExactOffset | 930.7 ns | 18.31 ns | 22.49 ns | 930.7 ns | 0.0277 |     120 B |

After:

           Method |     Mean |    Error |   StdDev | Allocated |
----------------- |---------:|---------:|---------:|----------:|
  ParseExactLocal | 101.3 ns | 2.205 ns | 3.091 ns |       0 B |
    ParseExactUtc | 349.8 ns | 5.253 ns | 4.656 ns |       0 B |
 ParseExactOffset | 357.7 ns | 3.720 ns | 3.106 ns |       0 B |

cc: @jkotas, @danmosemsft, @tarekgh, @joperezr, @joperezr
Contributes to https://github.com/dotnet/corefx/issues/30612

… format

Ports the code used by Utf8Parser, modified to support things Utf8Parser doesn't but DateTime{Offset{.ParseExact do, such as single-digit offset hours.
result.SetBadDateTimeFailure();
return false;
}
result.parsedDate = dateTime.AddTicks((long)Math.Round(fraction * Calendar.TicksPerSecond));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dateTime.AddTicks [](start = 32, length = 17)

is This possible to throw and we get different exception than what we used to get before?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By construction I don't believe that's possible, as DateTime.MaxValue is "9999-12-31T23:59:59.9999999", and by this point we've already validated that the DateTime of ""9999-12-31T23:59:59" was created successfully and validated against the calendar. We've also validated that none of the fraction digits are greater than 9, and "9999999" is the largest possible value this can be.

Is there another way this could manifest? We were very skimpy on coverage here, so I added a bunch of tests in dotnet/corefx#30860, including for MaxValue:
https://github.com/dotnet/corefx/pull/30860/files#diff-a7a9c52518e52358d4f3ab8e88cbf53fR963
but if you see something missing, I can rectify that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"9999-12-31T23:59:59.9999999Z" if you parse this when having TZ before UTC (e.g. Fuji +12) this should fail the parsing. in most cases, we'll get the right exception but there is will be specific values which can cause the exception only when adding the fraction ticks per second. I can try to get the exact data if you like.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should fail the parsing

Yes... but the factoring in of timezone doesn't happen until after this point, down at the bottom of the method where it calls DetermineTimeZoneAdjustments. At this point, we've called DateTime.TryCreate, using only the data from "9999-12-31T23:59:59", creating a DateTimeKind.Unspecified, and we add the "9999999" fraction to that. It's only after this AddTicks that we then parse the 'Z' and factor in the timezone as part of DetermineTimeZoneAdjustments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, we should be fine then. thanks for clarifying.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes me happy that 7,981 years from now (perhaps on another star system) our code will function correctly because we took care to correctly handle the year 9,999. 😃

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we're very forward thinking.

@stephentoub stephentoub merged commit e650d42 into dotnet:master Jul 6, 2018
@stephentoub stephentoub deleted the parseo branch July 6, 2018 16:12
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants