2

It seems that C# does not manage to parse a time in a valid RFC 3339 format:

DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)

This line throws an exception, while this line works just fine:

DateTime.ParseExact("2019-12-31T00:00:00.1234567+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffzzz", null)

So it seems there is a limit on milliseconds, but I cannot find out any documentation on that. Is this how it is supposed to be?


The reason want to parse this date is that I have have an input date field. We use OAS (Swagger) date-time format that quite clearly says that any date in RFC 3339 Internet Date/Time format should be valid. Now from the spec here section 5.6

time-secfrac    = "." 1*DIGIT

As far as I understand this means that up to 9 digits should be allowed and to be 100% compliant we have to allow these inputs, but it does not seem that C# even supports that.

Any ideas on how to fix it?

Ilya Chernomordik
  • 27,817
  • 27
  • 121
  • 207
  • 1
    it's simple, .NET supports up to 7 digit of the miliseconds – styx Oct 22 '19 at 14:12
  • IMHO, `1*DIGIT` means 1 or more digits. – Zohar Peled Oct 22 '19 at 14:13
  • That does not seem like a good spec with infinite amount of digits? But it's really hard to tell, I have not figured out exactly what does that mean – Ilya Chernomordik Oct 22 '19 at 14:14
  • 1
    On my system, `DateTime.Parse("2019-12-31T00:00:00.123456789+01:00", CultureInfo.InvariantCulture)` actually works and parses the value correctly (`DateTimeOffset` as well), using all the precision in the milliseconds it supports, even though there seems to be no way to specify an explicit format for it. So one possibility is a simple regex match to verify you've got this, and then parse it without `Exact`. – Jeroen Mostert Oct 22 '19 at 14:16
  • Then it's a bit of lying to say we accept this format to only drop some digits :) – Ilya Chernomordik Oct 22 '19 at 14:17
  • Regardless of parsing, `DateTime` does not have sufficient precision to support all those digits -- ticks are accurate to 100 ns intervals only. This limit is carried over from Windows itself. If you need to preserve this exactly, you can't use `DateTime` to begin with. – Jeroen Mostert Oct 22 '19 at 14:18
  • 1
    BTW, RFC 3339 follows ISO8601 specification - which states that the number of digits for the decimal fraction is not limited by standard, but needs to be agreed to by the communicating parties. – Zohar Peled Oct 22 '19 at 14:19
  • So 1*DIGIT means unlimited number of digits? – Ilya Chernomordik Oct 22 '19 at 14:21
  • 1
    Yes. 1*DIGIT means at least 1 digit and up to an unlimited number of digits. https://tools.ietf.org/html/rfc2234#section-3.6 – Robert McKee Oct 22 '19 at 14:24
  • @RobertMcKee: thanks for the link, I see now what it means, not that straightforward searching in these RFCs :) – Ilya Chernomordik Oct 22 '19 at 18:12

3 Answers3

2

Per MSDN specification, you can use only fffffff

The fffffff custom format specifier represents the seven most significant digits of the seconds fraction; that is, it represents the ten millionths of a second in a date and time value.

In your first example

DateTime.ParseExact("2019-12-31T00:00:00.123456789+01:00", "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffffzzz", null)

you are using fffffffff which is more precise for .NET custom date and time format strings

Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
1

As far as I know, .NET supports seven most significant digits for milliseconds which is The "fffffff" custom format specifier are for.

The "fffffff" custom format specifier represents the seven most significant digits of the seconds fraction; that is, it represents the ten millionths of a second in a date and time value.

Although it's possible to display the ten millionths of a second component of a time value, that value may not be meaningful. The precision of date and time values depends on the resolution of the system clock.

That means you are giving not meaningful data that are not supported for .NET Framework. I strongly suggest not doing that.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
0

In addition to the information in the other answers, if you cannot change your input and you still want to parse it, you may use one of the following solutions:

  1. If your input will always be in the same format (i.e., has 9 seconds-fraction digits), you could just remove the two extra ones and proceed to parse it:

    string input = "2019-12-31T00:00:00.123456789+01:00";
    input = input.Remove(27, 2);
    // TODO: parse `input`.
    
  2. If you don't know the number of the seconds-fraction digits beforehand, you may use something like this:

    string input = "2019-12-31T00:00:00.123456789+01:00";
    string format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFzzz";
    
    var regEx = new Regex(@"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,7})\d*");
    input = regEx.Replace(input, "$1");
    DateTime parsedDate = DateTime.ParseExact(input, format, null);