C# - Get a DateTime or DateTimeOffset in UTC format or a time zone format

Most APIs you come across use a standard for the format of dates and time, often it will be the ISO 8601 format, known as round-trip date/time pattern. You can get this standard format from a DateTime by providing the parameter "O" to a ToString call, however there are some gotchas to watch out for.

Different DateTimeKind gives different ToString("O") output

You can use the ToString method on the DateTime struct in the following way:

var dateTimeNowS = DateTime.Now.ToString("O");
//dateTimeNowS is "2022-01-21T22:06:21.9883794+02:00"

This gives you the format "2022-01-21T22:06:21.9883794+02:00" which has high precision and a timezone, the timezone is your local offset, for me in Denmark that is +02:00. Alternatively you could have defined the format yourself using the following, which would give the exact same result:

var dateTimeNowCustomS = DateTime.Now.
   ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK");
//dateTimeNowCustomS is "2022-01-21T22:06:21.9883794+02:00"

You can read more about creating specific formats for DateTime here.

You may have expected to get the UTC format, known as the "zero offset" format which is also easily recognizable due to the capital "Z" at the end. However the call to the ToString method of DateTime will only return this if the DateTime is of the kind "UTC". Internally the DateTime struct keeps track of whether it is a Local, UTC or Unspecified DateTime and the ToString method acts accordingly. If we had used UtcNow instead of .Now we would have gotten a UTC DateTime:

var dateTimeUtcNowS = DateTime.UtcNow.ToString("O");
//dateTimeUtcNowS is "2022-01-21T20:11:59.0102110Z"

As with the previous example you can specify the format on your own if you prefer:

var dateTimeNowUtcCustomS = DateTime.UtcNow.
   ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK");
//dateTimeNowUtcCustomS is "2022-01-21T20:11:59.0102110Z"

Due to the way that the K in the string provided for ToString operates, the timezone gets omitted on Datetimes in UTC time.

Get UTC format from local DateTime and vice versa

You can always change the type of the DateTIme from Local to UTC using the ToUniversalTime method as seen below:

var dateTimeNow = DateTime.Now;
var dateTimeNowS = dateTimeNow.ToString("O");
//dateTimeNowS is "2022-01-21T22:06:21.9883794+02:00"

var dateTimeNowUtc = dateTimeNow.ToUniversalTime(); //ToUniversalTime
var dateTimeNowUtcS = dateTimeNowUtc.ToString("O");
//dateTimeNowUtcS is "2022-01-21T20:06:21.9883794Z"

And you can get the reverse effect by calling the ToLocalTime method:

var dateTimeNowUtc = DateTime.UtcNow;
var dateTimeNowUtcS = dateTimeNowUtc.ToString("O");
//dateTimeNowUtcS is "2022-01-21T20:06:21.9883794Z"

var dateTimeNow = dateTimeNowUtc.ToLocalTime(); //ToLocalTime
var dateTimeNowS = dateTimeNow.ToString("O");
//dateTimeNowS is "2022-01-21T22:06:21.9883794+02:00"

DateTimeKind Unspecified

In addition to the UTC and local DateTimeKind there is also the Unspecified kind. A DateTime is for example unspecified if it is parsed from a string and not given a proper timezone or a UTC format. This way we have no chance of knowing what the timezone is meant to be and it is therefore "unspecified". The unspecified DateTime gives a third type of format when ToString("O") is called:

var dateTimeNowUnspecified = 
    DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified);
var dateTimeNowUnspecifiedS = dateTimeNowUnspecified.ToString("O");
//dateTimeNowUnspecifiedS is "2022-01-21T22:37:18.5056238"

DateTimeOffset format

Note for DateTimeOffset you always get the format with a timezone when calling ToString("O"):

var dateTimeOffsetNow = DateTimeOffset.Now;
var dateTimeOffsetNowS = dateTimeOffsetNow.ToString("O");
//dateTimeOffsetNowS is "2022-08-21T22:44:55.5370212+02:00"

var dateTimeOffsetUtcNow = DateTimeOffset.UtcNow;
var dateTimeOffsetUtcNowS = dateTimeOffsetUtcNow.ToString("O");
//dateTimeOffsetUtcNowS is "2022-08-21T20:44:55.5370212+00:00"

Whether you call UtcNow or just Now you will end up with the same format. As the DateTimeOffset is based on offsets rather than DateTimeKind. The offset will be different for the UtcNow as it will always give you the +00:00 adjusted offset.

You can get a "zero offset" format by converting it to a DateTime of UTC kind first:

var dateTimeOffsetNow = DateTimeOffset.Now;
var dateTimeOffsetNowS = dateTimeOffsetNow.UtcDateTime.ToString("O");
//dateTimeNowUtcS is "2022-01-21T20:55:33.0794136Z"

That is it

I hope you found this helpful. Microsoft has some great documentation on this topic, however it is rather long and overwhelming. Let me know in the comments down below what you think of this shorter version!