Почему DateTime.ToString(R) и DateTime.TryParseExact не работают в оба конца?

Я реализую условные запросы в веб-службе. Серверная часть может легко получить дату последнего изменения объекта, поэтому я отправляю Last-Modified и возвращаю If-Modified-Since. RFC для дат HTTP указывает формат, который совпадает с форматом < a href="https://msdn.microsoft.com/en-us/library/az4se3k1%28v=vs.110%29.aspx#RFC1123" rel="nofollow">описатель формата "R" в .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, чтобы преобразовать его в нужный мне формат, но как я могу заставить фреймворк использовать данные, которые уже есть?


person Steve Howard    schedule 09.10.2015    source источник


Ответы (1)


Я наткнулся на справочный источник, который объясняет это, поэтому задал и ответил на свой вопрос.

источник 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 является более правильным. Действительно, согласно документации по выбору между DateTime и DateTimeOffset:

Такое использование значений DateTimeOffset встречается гораздо чаще, чем использование значений DateTime. В результате DateTimeOffset следует считать типом даты и времени по умолчанию для разработки приложений.

Поэтому идеальное решение — переключиться на 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.

person Steve Howard    schedule 09.10.2015
comment
Да, это хорошо, за исключением того, что последнее Kind всегда будет Utc, поскольку вы получили его от .UtcDateTime. Но по крайней мере разбор правильный. Вам также будет интересно узнать, что DateTime.Parse действительно работает — по крайней мере, когда вы проходите DateTimeStyles.RoundtripKind. Только DateTime.ParseExact нет. - person Matt Johnson-Pint; 10.10.2015
comment
Конечно - вместо этого ужасного формата было бы лучше использовать ISO8601, но все же - хороший улов! - person Matt Johnson-Pint; 10.10.2015
comment
Кроме того, вы можете зарегистрировать это на странице github.com/dotnet/coreclr/issues — кажется глупо не исправить. - person Matt Johnson-Pint; 10.10.2015
comment
@MattJohnson, в комментарии говорится, что они не могут исправить это в целях совместимости, и я понимаю их точку зрения. И я согласен, что этот формат глупый (зачем включать день недели в поле, предназначенное для чтения компьютерами), но я пытался следовать IRC именно потому, что время от времени иметь спецификацию — это роскошь. ;) - person Steve Howard; 10.10.2015
comment
Спасибо, но я не согласен с сохранением этой ошибки. Я зарегистрировал это здесь: github.com/dotnet/coreclr/issues/1757 - person Matt Johnson-Pint; 13.10.2015
comment
Я думаю, что (надеюсь) не так много кода, который нужно взломать. Спасибо за подачу! - person Steve Howard; 13.10.2015