Search code examples
c#datetimedst

C# Datetime ISO 8601 format gives incorrect value


I am attempting to get a correctly formatted ISO 8601 date, and I keep hitting issues. My initial code seemed to be working, but I found that DST dates were not returning as expected. I made a .NET fiddle to ask about this issue here on stackoverflow, but it seems the way the "system" timezone works is going to cause me further problems when I deploy my code.

Here is a dotnet fiddle that displays something completely wrong:

using System;
                
public class Program
{
    public static void Main()
    {
        var val3 = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(2021, 10, 13, 18, 0, 0), TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time"));
        Console.WriteLine(val3.ToString("yyyy-MM-ddTHH:mm:sszzz"));
    
    }
}

If I run this, I get the following:

2021-10-13T13:00:00+00:00

So the time is correct for CST, but the offset looks like it is reflecting the "system" timezone on the server. Taken altogether, the date is completely different than the input date.

If I run this code on my development system where the local timezone is CST, I get yet another different answer:

2021-10-13T08:00:00-06:00

Given that the date in question was in DST, I expect both of the above to return the following:

2021-10-13T13:00:00-05:00

What I am doing wrong?


Solution

  • Let me see if my understanding is correct (I'm still not entirely sure when the SOAP comes into play in the question).

    You have a UTC DateTime:

    var dt = new DateTime(2021, 10, 13, 18, 0, 0, DateTimeKind.Utc);
    

    And you'd like to format that in CST -- which, on October 13, is actually CDT (-05:00)?

    If that is correct, then you want to utilise DateTimeOffset, which has a better understanding of.. well, time offsets.

    // Your original value. This will have an offset of "00:00" 
    // as the DateTime above was created as `DateTimeKind.Utc`
    var timeAtUtc = new DateTimeOffset(dt);
    
    // Alternatively:
    // var timeAtUtc = new DateTimeOffset(2021, 10, 13, 18, 0, 0, TimeSpan.Zero);
    
    // Find out the correct offset on that day (-5:00)
    var cstOffsetAtTheTime = TimeZoneInfo
        .FindSystemTimeZoneById("Central Standard Time")
        .GetUtcOffset(timeAtUtc);
    
    // And now apply the offset to your original value
    var timeAtCst = timeAtUtc.ToOffset(cstOffsetAtTheTime);
    
    // You will now get "2021-10-13T13:00:00-05:00" 
    // no matter where you run this from.
    Console.WriteLine(timeAtCst.ToString("yyyy-MM-ddTHH:mm:sszzz"));
    
    // Edit: If you pass it a date in Feb, you'll see that it will correctly be at -06:00.
    
    

    Edit 2022-03-07 based on comment below. If you don't need to care about the offset value, you can simply do:

    var timeAtUtc = ...;
    var timeAtCst = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(
      timeAtUtc, 
      "Central Standard Time");