'Convert DateTime to DateTimeOffset taking into account SummerTime

Consider the code below. I have a list of dates around the point in time when CEST changes from summer to winter time. I need to convert them to UTC. However using this code the midnight gets lost, I can't understand how to fix it.

DateTime firstDate = new DateTime(2020, 10, 24, 21, 0, 0);
DateTime lastDate = new DateTime(2020, 10, 25, 3, 0, 0);
DateTime[] dates = Enumerable.Range(0, (lastDate - firstDate).Hours + 1)
    .Select(r => firstDate.AddHours(r))
    .ToArray();

TimeZoneInfo tzo = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
List<DateTimeOffset> offsets = new List<DateTimeOffset>();
foreach(var date in dates)
{
    var timeSpan = tzo.GetUtcOffset(date);
    var offset = new DateTimeOffset(date, timeSpan);
    offsets.Add(offset);
}
10/24/2020 09:00:00 PM +02:00 = 19:00Z
10/24/2020 10:00:00 PM +02:00 = 20:00Z
10/24/2020 11:00:00 PM +02:00 = 21:00Z
10/25/2020 12:00:00 AM +02:00 = 22:00Z
10/25/2020 01:00:00 AM +02:00 = 23:00Z
10/25/2020 02:00:00 AM +01:00 = 01:00Z - What happened to 00:00Z?
10/25/2020 03:00:00 AM +01:00 = 02:00Z


Solution 1:[1]

The problem is that there are two 2AM's on 25th October 2020 - 02:00 AM +02:00 and 02:00 AM +01:00.

Since the source DateTime has no offset available, the GetUtcOffset method has no way to determine which 2AM the value refers to, and so it defaults to using the non-DST offset - 02:00 AM +01:00.

Depending on what you're actually trying to do, you may be able to resolve this by converting the start time to UTC, generating a list of UTC DateTimeOffset values, and then converting them back to the desired time-zone:

DateTime firstDate = new DateTime(2020, 10, 24, 21, 0, 0);
TimeZoneInfo tzo = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTimeOffset firstDateUtc = TimeZoneInfo.ConvertTime(firstDate, tzo, TimeZoneInfo.Utc);
DateTimeOffset[] utcDates = Enumerable.Range(0, 7).Select(r => firstDateUtc.AddHours(r)).ToArray();
DateTimeOffset[] offsets = Array.ConvertAll(utcDates, d => TimeZoneInfo.ConvertTime(d, tzo));

Output:

24/10/2020 21:00:00 +02:00 
24/10/2020 22:00:00 +02:00 
24/10/2020 23:00:00 +02:00 
25/10/2020 00:00:00 +02:00 
25/10/2020 01:00:00 +02:00 
25/10/2020 02:00:00 +02:00 
25/10/2020 02:00:00 +01:00 

Alternatively, you might want to look at NodaTime, which provides a much clearer model for working with dates and times.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Richard Deeming