Home > Net >  Comparing DateTime objects in C# when clocks turn
Comparing DateTime objects in C# when clocks turn

Time:03-04

It seems like the comparison of DateTime objects does not work by default when the clocks turn for daylight savings. My end goal is to check if a list of times are in ascending order. I have the following code to demonstrate my issue: (I live in the UK)

// Create a list of strings
List<string> timeStrings = new List<string>
{
    "2023-10-29 00:00:00.000  00:00",
    "2023-10-29 00:30:00.000  00:00",
    "2023-10-29 01:00:00.000  00:00",
    "2023-10-29 01:30:00.000  00:00",
    "2023-10-29 02:00:00.000  00:00",
};

// Convert to a list of DateTimes
List<DateTime> times = new List<DateTime>();
foreach (string str in timeStrings)
{
    DateTime dateTime;
    DateTime.TryParseExact(str, "yyyy-MM-dd HH:mm:ss.FFF zzz", null, DateTimeStyles.None, out dateTime);
    times.Add(dateTime);
}

// Print the results
for (int i = 0; i < times.Count; i  )
{
    string timeString = timeStrings[i];
    DateTime time = times[i];
    Console.WriteLine(timeString   " -> "   time.ToString());
    if (i < times.Count - 1)
    {
        Console.WriteLine(times[i] < times[i   1] ? "/\\" : "\\/");
    }
}

The output of this code shows this ordering (\/ indicates that the top is bigger):

2023-10-29 00:00:00.000  00:00 -> 29/10/2023 01:00:00
/\
2023-10-29 00:30:00.000  00:00 -> 29/10/2023 01:30:00
\/
2023-10-29 01:00:00.000  00:00 -> 29/10/2023 01:00:00
/\
2023-10-29 01:30:00.000  00:00 -> 29/10/2023 01:30:00
/\
2023-10-29 02:00:00.000  00:00 -> 29/10/2023 02:00:00

The strange thing is that when I use .ToUniversalTime() to convert the times before comparing them, I get the order that I want. This implies that all the information to determine the true order is within the DateTime objects.

When I read the documentation, I could only find that the compared DateTime objects must have the same timezone, but in this case they do, and the results still seem to be incorrect with my current understanding.

CodePudding user response:

First, be aware that the code you've posted won't reproduce the issue for people in other time zones, since DST begins at different times for them (if it happens at all).

Second, be aware that the strings you're starting with represent moments in time whereas a DateTime only captures three pieces of information:

  • A calendar date
  • A time of day (wall-clock)
  • A "Kind": UTC, Local, or Unknown

Since the "Local" on Kind doesn't include any information about where that date is local to, converting your moments in time to DateTimes is a lossy operation: you're losing important data that you can't retrieve later.

That means the two entries that get translated to 29/10/2023 01:00:00 are completely indistinguishable from one another. They are both less than 29/10/2023 01:30:00: you can't have one be greater than that time and the other be less than it. If you ask the framework if one of those dates IsDaylightSavingTime(), it will only tell you its best guess based on the information it has: it cannot tell the difference between the 1 AM that occurred before the DST switch and the 1 AM that occurred after the DST switch.

The solution is to translate your strings into a type that accurately captures a moment in time. The built-in class for that is DateTimeOffset. It's generally wise to use this type any time you're representing a point in time, rather than a user-selected date and time. When displaying the timestamp to users, you can easily convert that value back to a localized time based on the user's locality settings.

If you need even more flexibility, the NodaTime library includes several more nuanced classes like Instant, which are appropriate for using in a variety of specialized situations.

  • Related