C# - .net core DateTime conversion to UTC minutes offset

Viewed 485

I was testing a code and found a very strange behavior. When I use DateTime.MinValue, add minutes to it, and convert it to UTC, there appears to be a difference of 58 minutes between my local time zone and UTC. I am in central Europe (+1 in winter or +2 in summer). How can this be 58 minutes? I would expect 1 hour offset not 58 minutes. I am using .net core on Linux.

var x1 = DateTime.MinValue.AddMinutes(61);
var x1ticks = x1.Ticks;
var x1kind = x1.Kind;
var y1 = x1.ToUniversalTime();
var y1tickes = y1.Ticks;
var y1kind = y1.Kind;

var z1 = TimeZoneInfo.ConvertTimeToUtc(x1, TimeZoneInfo.Local);
var z1ticks = z1.Ticks;
var z1kind = z1.Kind;

code snippet with evaluated values: code snippet with evaluated values

1 Answers

58 minutes is approximately the offset from UTC of the local mean time (Wikipedia) of Prague, Czech Republic. My guess is that your local timezone is Europe/Prague.

TimeZoneInfo looks up the timezone data from the timezone database on your computer. On a Linux machine (and all POSIX ones, I think) the default is the IANA tz Database. On Windows computers you'd be querying the Windows timezone database by default.

Anyway, the important thing is, according to the IANA database, the UTC offset of your local timezone at DateTime.MinValue plus 61 minutes, is plus 58 minutes.

// prints 00:58:00
Console.WriteLine(TimeZoneInfo.FindSystemTimeZoneById("Europe/Prague").GetUtcOffset(DateTime.MinValue));

See also this post.

The IANA database actually says that the offset is 57 minutes and 44 seconds, but .NET Core actually rounds it as an implementation detail, because it doesn't support seconds. I found this line of the source code that explains it:

    private static TimeSpan TZif_CalculateTransitionOffsetFromBase(TimeSpan transitionOffset, TimeSpan timeZoneBaseUtcOffset)
    {
        TimeSpan result = transitionOffset - timeZoneBaseUtcOffset;

        // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns
        // with DateTimeOffset, SQL Server, and the W3C XML Specification
        if (result.Ticks % TimeSpan.TicksPerMinute != 0)
        {
            result = new TimeSpan(result.Hours, result.Minutes, 0);
        }

        return result;
    }
Related