Home > Back-end >  Convert DateTime to DateTimeOffset taking into account SummerTime
Convert DateTime to DateTimeOffset taking into account SummerTime

Time:02-03

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

CodePudding user response:

You're converting from what I'd call a "local date/time" to a UTC value. Some local date/time values are skipped and some are repeated, due to daylight saving transitions (and other time zone changes).

In the situation you're showing, every local time between 2am inclusive and 3am exclusive happened twice, because at 3am (the first time) the clocks went back to 2am - this line:

10/25/2020 02:00:00 AM  01:00 = 01:00Z

... shows the second mapping of 2am. But at 2020-10-25T00:00:00Z the local time was also 2am due to the clocks going back. Your conversion is ambiguous, in other words.

The TimeZoneInfo documentation states:

If dateTime is ambiguous, or if the converted time is ambiguous, this method interprets the ambiguous time as a standard time.

Here, "standard time" is the second occurrence of any ambiguous time (because it's a transition from daylight time to standard time).

Fundamentally, if you only have local values then you have incomplete information. If you want to treat ambiguous values as daylight time instead of standard time (or flag them up as ambiguous), you can always use TimeZoneInfo.IsAmbiguousTime(DateTime) to detect that.

CodePudding user response:

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.

  •  Tags:  
  • Related