为什么 DateTime.ToString("R") 和 DateTime.TryParseExact 不往返?

Why don't DateTime.ToString("R") and DateTime.TryParseExact round trip?

我正在 Web 服务中实现条件请求。后端可以轻松检索实体的最后修改日期,因此我发送 Last-Modified 并返回 If-Modified-Since。 RFC for HTTP Dates specifies a format that is the same as the "R" format specifier in .NET.

问题是 DateTime.ToString("R") 正确地格式化了日期,但是将 "R" 传递给 ParseExact 并没有读回时区(有一个“往返”说明符, "O",但不是我需要的格式)。这是 LinqPad 中的示例:

DateTime lastModified = new DateTime(2015, 10, 01, 00, 00, 00, DateTimeKind.Utc);
string lastModifiedField = lastModified.ToString("R"); // Thu, 01 Oct 2015 00:00:00 GMT
DateTime ifModifiedSince = DateTime.ParseExact(
   lastModifiedField, "R", CultureInfo.InvariantCulture);

ifModifiedSince.Kind.Dump(); // Unspecified

我当然可以在已解析的 DateTime 上使用方法将其强制为我想要的格式,但是我怎样才能让框架使用已经存在的数据?

我偶然发现了解释这一点的参考资料,因此提出并回答了我自己的问题。

source to datetimeparse.cs 表示这是一个无法修复的错误。

// The "r" and "u" formats incorrectly quoted 'GMT' and 'Z', respectively.  We cannot
// correct this mistake for DateTime.ParseExact for compatibility reasons, but we can 
// fix it for DateTimeOffset.ParseExact as DateTimeOffset has not been publically released
// with this issue.

因此 DateTime.ParseExact 和 DateTimeOffset.ParseExact 都调用了此评论之前的代码,并且实际上表明 DateTimeOffset.ParseExact 更正确。事实上,根据 choosing between DateTime and DateTimeOffset 上的文档:

These uses for DateTimeOffset values are much more common than those for DateTime values. As a result, DateTimeOffset should be considered the default date and time type for application development.

因此,理想的解决方案是切换到 DateTimeOffset,但如果您仍然需要 DateTime:

DateTime lastModified = new DateTime(2015, 10, 01, 00, 00, 00, DateTimeKind.Utc);
string lastModifiedField = lastModified.ToString("R");
DateTimeOffset ifModifiedSinceOffset = DateTimeOffset.ParseExact(
   lastModifiedField, "R", CultureInfo.InvariantCulture);
DateTime ifModifiedSince = ifModifiedSinceOffset.UtcDateTime;

ifModifiedSince.Kind.Dump(); // Utc

正确地将时区标识为 GMT/UTC,从而在 DateTime 上设置正确的 属性。