Click here to Skip to main content
15,886,026 members
Articles / Programming Languages / C#
Tip/Trick

DateTime.TryParse and the Case of Z Letter

Rate me:
Please Sign up or sign in to vote.
4.72/5 (9 votes)
8 May 2021CPOL2 min read 9.5K   11   4
Explanation of how DateTime.TryParse behaves with ISO-8601 format
This short tip aims to fill the gap in docs explaining what is ISO-8601 date format, how it is treated by DateTime.TryParse compared to other formats

Introduction

Recently, I’ve been tasked to provide date in a specific format from backend to the frontend and I noticed a behavior that I found a bit odd.

C#
private static void OutputDateInfo(string value)
{
    Console.WriteLine($"Input: {value}");
    if (DateTime.TryParse(value, out DateTime dateTimeValue))
    {
        Console.WriteLine($"Setialized to universal format 
                {dateTimeValue.ToString("yyyy-MM-dd'T'HH:mm:ssZ")}");
    }
    Console.WriteLine();
}

//somewhere in main
OutputDateInfo("2021-04-15T21:01:00.0000000Z");
OutputDateInfo("2021-04-15T21:01:00.0000000");

produces the output:

Input: 2021-04-15T21:01:00.0000000Z
Setialized to universal format 2021-04-16T00:01:00Z

Input: 2021-04-15T21:01:00.0000000
Setialized to universal format 2021-04-15T21:01:00Z

So as I was in Kyiv which is in UTC+3 timezone it looked as letter Z forced ToString method to convert date to local time.

As documentation reads:

Quote:

If s contains no time zone information, result contains a DateTime value whose Kind property is DateTimeKind.Unspecified when the method returns. If the string to be parsed contains time zone information, result contains a DateTime value whose Kind property is DateTimeKind.Local when the method returns.

Is this DateTimeKind that makes ToString cast date to the local timezone? Let’s log more properties to see if it is the case:

C#
private static void OutputDateInfo(string value)
{
    Console.WriteLine($"Input: {value}");
    if (DateTime.TryParse(value, out DateTime dateTimeValue))
    {
        Console.WriteLine($"Setialized to universal format 
                         {dateTimeValue.ToString("yyyy-MM-dd'T'HHssZ")}");
        Console.WriteLine($"Setialized to default format {dateTimeValue}");
        Console.WriteLine($"Setialized with conversion to universal time 
                         {dateTimeValue.ToUniversalTime()}");
        Console.WriteLine($"Kind: {dateTimeValue.Kind}");
    }
    Console.WriteLine();
}

Now we see in a console:

Input: 2021-04-15T21:01:00.0000000Z
Setialized to universal format 2021-04-16T00:01:00Z
Setialized to default format 16.04.2021 0:01:00
Setialized with conversion to universal time 15.04.2021 21:01:00
Kind: Local

Input: 2021-04-15T21:01:00.0000000
Setialized to universal format 2021-04-15T21:01:00Z
Setialized to default format 15.04.2021 21:01:00
Setialized with conversion to universal time 15.04.2021 18:01:00
Kind: Unspecified

So indeed when we call ToString on a DateTimeKind.Local instance, it will be adjusted according to server timezone.

But why letter Z is treated as timezone information? The documentation referred to above has no example with Z letter. The answer is that format in question is ISO-8601 format and Z stands for “Zero UTC offset”. Is string of this format treated just as any other string with timezone specified or is this some kind of special treatment? We’ll get an answer by comparing them.

C#
OutputDateInfo("2021-04-15T2100.0000000Z");
OutputDateInfo("2021-04-15T2100.0000000");
OutputDateInfo("2021-04-15T1400.0000000 -7:00");

yields:

Input: 2021-04-15T21:01:00.0000000Z
Setialized to universal format 2021-04-16T00:01:00Z
Setialized to default format 16.04.2021 0:01:00
Setialized with conversion to universal time 15.04.2021 21:01:00
Kind: Local

Input: 2021-04-15T21:01:00.0000000
Setialized to universal format 2021-04-15T21:01:00Z
Setialized to default format 15.04.2021 21:01:00
Setialized with conversion to universal time 15.04.2021 18:01:00
Kind: Unspecified

Input: 2021-04-15T14:01:00.0000000 -7:00
Setialized to universal format 2021-04-16T00:01:00Z
Setialized to default format 16.04.2021 0:01:00
Setialized with conversion to universal time 15.04.2021 21:01:00
Kind: Local

As you can see, string of ISO-8601 format is treated just as any other date string with timezone specified.

My intent though wasn’t to treat it as local time but instead as UTC time. My goal was to provide universal time to front end so it will adjust it to end-user’s local time. How can we trick .NET into thinking that this is universal time without any timezone? The answer is the overload that accepts DateTimeStyles:

C#
private static void OutputDateInfo(string value)
{
    Console.WriteLine($"Input: {value}");
    if (DateTime.TryParse(value, null, DateTimeStyles.AdjustToUniversal, 
                          out DateTime universalTime))
    {
        Console.WriteLine($"Adjusted to universal {universalTime}");
    }

    Console.WriteLine();
}

outputs:

Input: 2021-04-15T21:01:00.0000000Z
Adjusted to universal 15.04.2021 21:01:00

Input: 2021-04-15T21:01:00.0000000
Adjusted to universal 15.04.2021 21:01:00

Input: 2021-04-15T14:01:00.0000000 -7:00
Adjusted to universal 15.04.2021 21:01:00

Job done!

Conclusion

In my opinion, DateTimeKind is underrepresented in literature and blogs. However, this is the thing that should definitely be taken into account when parsing date strings and converting dates back to string. Also, it is worth remembering that ISO-8601 format date strings are treated just as any other string with timezone info specified, although it might be unobvious at first glance.

History

  • 8th May 2021 - Published initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader
Ukraine Ukraine
Team leader with 8 years of experience in the industry. Applying interest to a various range of topics such as .NET, Go, Typescript and software architecture.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Rusty Bullet10-May-21 10:06
Rusty Bullet10-May-21 10:06 
Well done.
GeneralRe: My vote of 5 Pin
Bohdan Stupak11-May-21 22:01
professionalBohdan Stupak11-May-21 22:01 
QuestionSerialized to universal format Pin
Riz Thon9-May-21 22:19
Riz Thon9-May-21 22:19 
AnswerRe: Serialized to universal format Pin
Bohdan Stupak11-May-21 22:00
professionalBohdan Stupak11-May-21 22:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.