I was reading into answerer's post here where I ran into this enumerator DateTimeStyles.RoundtripKind
which I'm trying to understand. I looked into MSDN here which says:
The DateTimeKind field of a date is preserved when a DateTime object is converted to a string using the "o" or "r" standard format specifier, and the string is then converted back to a DateTime object.
The timestamp in the input in the post I referred is like this:
<timestamp time='2016-09-16T13:45:30'>
I ran her code and it still works. Now it is all a mess to connect all the information I have:
- The above time stamp contains some identifier
T
- The MSDN documentation talks about
o
andr
format specifiers which it doesn't tell what it is? If you go into dig more details on
DateTimeKind
enumeration on the MSDN link I've quoted above it says nothing abouto
andr
format specifiers. Here is the link which says:Member Name | Description -------------------------------------------------------------------------------- Local | The time represented is local time. Unspecified | The time represented is not specified as either local time or Coordinated Universal Time (UTC). Utc | The time represented is UTC.
P.S. I tried creating a table above but it seems SO has no native support for creating tabular structures.
So can someone help me understand DateTimeStyles.RoundtripKind
enumeration and how it works?
So I was finally able to understand this and sharing the same information here if it can be helpful for others too:
First part is conversion of C# DateTime object into string. There are many format specifiers to do that but for us "r" and "o" format specifiers are of concern to us with regards to
DateTimeStyles.RoundtripKind
. You can see all date time format specifiers here. See what happens when we do the conversion in code using these format specifiers:You can clearly see that string date time being output has the information embedded inside it which suggests:
Fri, 23 Sep 2016 15:39:21 GMT
is ofDateTimeKind.Utc
("GMT" text is present)2016-09-23T15:39:21.8899216+05:30
represents a date time ofDateTimeKind.Local
("T" character is present as perISO 8601
standard)Now comes the second part. If I've to convert these date time strings
gmtDateTimeString
andlocalDateTimeString
back to a date time object then we need to parse them. So with the help ofDateTimeStyles.RoundtripKind
enumeration value passed toDateTime.Parse
API you actually signify that time zone information is already baked in the string and API parses the date time appropriately using that information.Normally when date time data is transferred over the wire in XML format then ISO 8601 format is used which I saw in the post which I referred before posting the question in this thread. So while parsing such a date time string obtained from an XML document it was appropriate to use the
DateTimeStyles.RoundtripKind
to get the right date time value as per the time-zone information present in the string.The roundtrip format is meant for "machine consumption" - it can easily be parsed back into the same DateTime value.
Most of the other formats are for "human consumption", to show the date (possibly including time) to a person.
I had a hard time understanding the other answers so I decided to do some resarch myself. Luckily, the source code for the .NET library is available online.
DateTimeStyles.RoundTripKind
has a comment in the source:It is more or less just as vague as the MSDN documentation on
DateTimeStyles.RoundTripKind
:By navigating the Reference Source website it can be seen that
DateTimeStyles.RoundTripKind
is used very little. Essentially, if the flag is set then it may modify the kind of theDateTime
toDateTimeKind.Utc
. So this is the effect of setting this flag: Sometimes theKind
property of the parsedDateTime
value is set toUtc
.Exactly when this happens is controlled by the internal flag
ParseFlags.TimeZoneUtc
. It is more complicated to determine when this flag gets set but as far as I can tell the parser will set this flag if the timezone is specified using eitherZ
orGMT
. There is a comment about this in the source code:My conclusion is that if a timestamp is formatted using either
o
orr
andDateTimeStyles.RoundTripKind
is used while parsing the timestamp then theKind
of the resultingDateTime
value is set toUtc
if the timezone in the string is the UTC timezone.However, what happens when the flag is not set? The best way to determine this is to do some actual testing of the two format specifiers.
The Round-trip ("O", "o") format specifier
When using the
o
format specifier the timezone of the timestamp will either beZ
for UTC or+/-
the offset from UTC (e.g.2017-02-26T22:55:15.4923368+01:00
). Here is a table that shows the value of theKind
property of aDateTime
value parsed from a round-trip timestamp:If you want to parse a timestamp in round-trip format and you expect the timezone of the timestamp to be UTC then you should specify
DateTimeStyles.RoundTripKind
to ensure that the parsedDateTime
value has kindUtc
.The RFC1123 ("R", "r") format specifier
When using the
r
format specifier the timestamp will always containGMT
(even if the kind of the originalDateTime
is notUtc
) thus a table for ther
format has no need for aTimezone
column. However, I have discovered thatDateTime.Parse
andDateTime.ParseExact
behave differently when a RFC1123 timestamp is parsed:When using the
Parse
method a timestamp in the RFC1123 format behaves the same as a UTC timestamp in the round-trip format. However, for some reason theParseExact
method ignores theDateTimeStyles.RoundTripKind
flag. This is not the case when a round-trip formatted timestamp is parsed.If you want to parse a timestamp in RFC1123 format you should either use the
Parse
method and specifyDateTimeStyles.RoundTripKind
or if you prefer theParseExact
method you will have to modify the kind of the parsed timestamp toUtc
. You do that by creating a new timestamp using theDateTime.SpecifyKind
method.Conclusion
When parsing round-trip and RFC1123 timestamps specify
DateTimeStyles.RoundTripKind
to ensure that theKind
property of the parsedDateTime
value isUtc
.If a round-trip timestamp has a non-zero offset then you will have to parse the timestamp into a
DateTimeOffset
value to preserve the offset (Local
does not tell you what the offset is - just that it probably is different from 0).Do not use
DateTime.ParseExact
to parse RFC1123 timestamps (or change the kind toUtc
after the timestamp has been parsed).