C# - .net core DateTime 转换为 UTC 分钟偏移量

问题描述

我在测试代码时发现了一个非常奇怪的行为。当我使用 DateTime.MinValue 时,为其添加分钟并将其转换为 UTC,我的本地时区和 UTC 之间似乎存在 58 分钟的差异。我在中欧(冬季 +1 或夏季 +2)。这怎么可能是58分钟?我希望 1 小时偏移而不是 58 分钟。我在 Linux 上使用 .net core。

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

解决方法

58 分钟大约是捷克共和国布拉格 local mean time (Wikipedia) 与 UTC 的偏移量。我的猜测是您当地的时区是 Europe/Prague

TimeZoneInfo 从您计算机上的时区数据库中查找时区数据。在 Linux 机器上(以及所有 POSIX 机器,我认为)默认是 IANA tz Database。在 Windows 计算机上,默认情况下您将查询 Windows 时区数据库。

无论如何,重要的是,根据 IANA 数据库,您本地时区在 DateTime.MinValue 处的 UTC 偏移量加上 61 分钟,就是加上 58 分钟。

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

另见this post

IANA 数据库实际上说偏移量为 57 分 44 秒,但 .NET Core 实际上将其作为实现细节进行了舍入,因为它不支持秒。我找到了解释它的 line 源代码:

    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;
    }