What does DateTimeStyles.RoundtripKind enumeration mean?
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:
//r corresponds to RFC 1123 format (GMT date time format)
var gmtDateTimeString = DateTime.Now.ToString("r"); //gives Fri, 23 Sep 2016 15:39:21 GMT
//o corresponds to ISO 8601 (Local date time format)
var localDateTimeString = DateTime.Now.ToString("o"); //gives 2016-09-23T15:39:21.8899216+05:30
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 of DateTimeKind.Utc (GMT text is present)
- 2016-09-23T15:39:21.8899216+05:30 represents a date time of DateTimeKind.Local (T character is present as per ISO 8601 standard)
Now comes the second part. If I've to convert the date time strings stored in gmtDateTimeString
and localDateTimeString
back to a date time object then we need to parse them. So with the help of DateTimeStyles.RoundtripKind enumeration value passed to DateTime.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.
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:
// Attempt to preserve whether the input is unspecified, local or UTC
It is more or less just as vague as the MSDN documentation on DateTimeStyles.RoundTripKind
:
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.
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 the DateTime
to DateTimeKind.Utc
. So this is the effect of setting this flag: Sometimes the Kind
property of the parsed DateTime
value is set to Utc
.
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 either Z
or GMT
. There is a comment about this in the source code:
// NOTENOTE : for now, we only support "GMT" and "Z" (for Zulu time).
My conclusion is that if a timestamp is formatted using either o
or r
and DateTimeStyles.RoundTripKind
is used while parsing the timestamp then the Kind
of the resulting DateTime
value is set to Utc
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 be Z
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 the Kind
property of a DateTime
value parsed from a round-trip timestamp:
Timezone | RoundTripKind | Kind ---------+---------------+------ "Z" | Not specified | Local "Z" | Specified | Utc Not "Z" | Not specified | Local Not "Z" | Specified | Local
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 parsed DateTime
value has kind Utc
.
The RFC1123 ("R", "r") format specifier
When using the r
format specifier the timestamp will always contain GMT
(even if the kind of the original DateTime
is not Utc
) thus a table for the r
format has no need for a Timezone
column. However, I have discovered that DateTime.Parse
and DateTime.ParseExact
behave differently when a RFC1123 timestamp is parsed:
Method | RoundTripKind | Kind -----------+---------------+------------ Parse | Not specified | Local Parse | Specified | Utc ParseExact | Not specified | Unspecified ParseExact | Specified | Unspecified
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 the ParseExact
method ignores the DateTimeStyles.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 specify DateTimeStyles.RoundTripKind
or if you prefer the ParseExact
method you will have to modify the kind of the parsed timestamp to Utc
. You do that by creating a new timestamp using the DateTime.SpecifyKind
method.
Conclusion
When parsing round-trip and RFC1123 timestamps specify DateTimeStyles.RoundTripKind
to ensure that the Kind
property of the parsed DateTime
value is Utc
.
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 to Utc
after the timestamp has been parsed).