6

This (LINQPad snippet):

DateTime.Now.ToString("yyyy-MM-ddTHH:mm:sszzz").Dump();
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz").Dump();

Returns:

2016-01-08T09:05:04-07:00 // Expected
2016-01-08T16:05:04-07:00 // Not what I expected

Given that the 2nd result is universal time, I expected it to return 2016-01-08T16:05:04-00:00 (timezone offset of zero).

Am I missing something or is this a bug?

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Anders
  • 15,227
  • 5
  • 32
  • 42
  • 4
    If you try that in a console app it throws an exception as the UtcNow cannot use the specified format as it does not have a zone... so the format is invalid for UtcNow, and there is some bug in LINQPad which is ignoring that – Gusman Jan 08 '16 at 16:23
  • 2
    If you use `DateTimeOffset` instead of `DateTime`, it will be correct. – Evan Mulawski Jan 08 '16 at 16:25
  • Thank you! Between these two comments I have an explanation and a workaround, and I'm happy to accept either as an answer if you want to put it in one. – Anders Jan 08 '16 at 16:32
  • @Gusman I _don't_ think this is a bug in LINQPad. Please take a look my answer. – Soner Gönül Jan 13 '16 at 13:17

1 Answers1

9

I wanna combine Gusman's and Evan's comments and create some arguments against them if they let me. Let's take step by step..

Should DateTime.UtcNow have a timezone offset?

No matter what Kind it has (Local, Utc or Unspecified), a DateTime instance does not keep any UTC offset value. Period.

First of all, let's take a look at the The "zzz" format specifier documentation;

With DateTime values, the "zzz" custom format specifier represents the signed offset of the local operating system's time zone from UTC, measured in hours and minutes. It does not reflect the value of an instance's DateTime.Kind property. For this reason, the "zzz" format specifier is not recommended for use with DateTime values.

Based on first two bold sentences, both result generated by LINQPad is totally expected. You can get the same results (expect date and time parts of course) in Visual Studio as well.

Just run this code as "Start Without Debugging" (Ctrl + F5)

Console.WriteLine(DateTime.Now.ToString("yyyy-MM-ddTHH:mm:sszzz"));
Console.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz"));

But what about when you run it as "Start Debugging" with (F5)?

There are two option in such a case which depends on your DateTimeInvalidLocalFormat in Managed Debugging Assistants is ticked or not. This sections is on the Debug menu, then click Exceptions.

enter image description here

If this option is ticked, you get an exception and it says;

A UTC DateTime is being converted to text in a format that is only correct for local times. This can happen when calling DateTime.ToString using the 'z' format specifier, which will include a local time zone offset in the output. In that case, either use the 'Z' format specifier, which designates a UTC time, or use the 'o' format string, which is the recommended way to persist a DateTime in text.

If this option is not ticked, you won't get any message and the result will be the same as LINQPad.

Given that the 2nd result is universal time, I expected it to return 2016-01-08T16:05:04-00:00 (timezone offset of zero).

As I explained, zzz format specifier does not behave like that for any DateTime instance.

But it behaves differently for a DateTimeOffset instance which is documented as;

With DateTimeOffset values, this format specifier represents the DateTimeOffset value's Offset from UTC in hours.

And DateTimeOffset.Now and DateTimeOffset.UtcNow are perfectly fits your expectation no matter you run in LINQPad or Visual Studio since they are documented as;

Now

A DateTimeOffset object whose date and time is the current local time and whose offset is the local time zone's offset from Coordinated Universal Time (UTC).

UtcNow

An object whose date and time is the current Coordinated Universal Time (UTC) and whose offset is TimeSpan.Zero.

Console.WriteLine(DateTimeOffset.Now.ToString("yyyy-MM-ddTHH:mm:sszzz"));
Console.WriteLine(DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz"));

would generate

2016-01-08T09:05:04-07:00
2016-01-08T16:05:04-00:00

Tl;dr I think this is not a bug. LINQPad generates the right results exactly what it should be. In Visual Studio, you might get an exception if that option is ticked or not in debugging mode.

Community
  • 1
  • 1
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364