runtime: `DateTime.Now.ToString("r")` incorrectly attributes GMT
Description
DateTime.Now.ToString("r")
stamps the LOCAL time but appends GMT
on the string, resulting in an INCORRECT output value.
Console.WriteLine(DateTime.Now.ToString("r"));
Console.WriteLine(DateTime.UtcNow.ToString("r"));
Console.WriteLine(DateTimeOffset.Now.ToString("r"));
Console.WriteLine(DateTimeOffset.UtcNow.ToString("r"));
produces:
Thu, 18 Aug 2022 14:33:58 GMT
Thu, 18 Aug 2022 21:33:59 GMT
Thu, 18 Aug 2022 21:33:59 GMT
Thu, 18 Aug 2022 21:33:59 GMT
The first line is factually incorrect, yet what the code produces.
Documentation points to the following note:
Although the RFC 1123 standard expresses a time as Coordinated Universal Time (UTC), the formatting operation does not modify the value of the DateTime object that is being formatted. Therefore, you must convert the DateTime value to UTC by calling the DateTime.ToUniversalTime method before you perform the formatting operation. In contrast, DateTimeOffset values perform this conversion automatically; there is no need to call the DateTimeOffset.ToUniversalTime method before the formatting operation. DateTime Struct (System) Represents an instant in time, typically expressed as a date and time of day.
The documentation effectively reads like we documented an unfixed bug.
Reproduction Steps
Console.WriteLine(DateTime.Now.ToString("r"));
Expected behavior
Would produce either a) the actual GMT correct value or b) the correct timezone noting – but not a mix of both resulting in an incorrect value.
Actual behavior
Local time with a GMT
timezone stamp which is wrong
Regression?
No response
Known Workarounds
Don’t use DateTime.Now
, use DateTime.UtcNow
or DateTimeOffset
Configuration
.NET 7
Other information
No response
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 27 (26 by maintainers)
going back to the original issue, I already discussed this a few days ago with @timheuer. My take here is not to change anything as this is the behavior since day one and we are clearly documenting the behavior as indicated. It is not worth the app-compat breaks especially since we have some easier alternatives. I suggest closing this issue.
If that’s the case, probably best to stick to ISO 8601 (“o”) format, with the invariant culture.
Excellent question @danmoseley – the real answer is indeed legacy/muscle memory and IntelliSense offering…oh and the fact the API seems clear to me – I want to work with Dates and Times…hmmm,
DateTime
seems to be a great API and it has everything I need aNow
and all sorts of things. It isn’t intuitive to me thatDateTimeOffset
is a ‘better’ thing thanDateTime
– that’s my real answer.As far as the
"r"
format specifier goes - if we were wanting to make a breaking change for .NET 7, then we could do the following:These would all still be compliant with RFC 1123/822/2822/5322 We’d then have to document that we were making a breaking change to provide better compliance with the spec, but grandfathering in that
DateTimeKind.Unspecified
is treated asDateTimeKind.Utc
.Note on the last one, I think it’s important to also stay compliant with the HTTP Date Header in RFC 7231 for
DateTimeOffset.UtcNow
, so we would emitGMT
instead of+0000
. The trade-off is that there are local times that are also UTC+0 (such as England in the winter, or Iceland year-round) that would see “GMT” instead of their local offset.