代码之家  ›  专栏  ›  技术社区  ›  Norcino

TimeZoneInfo支持DylightSavingTime没有返回我所期望的,为什么?

  •  0
  • Norcino  · 技术社区  · 5 年前

    当我开始研究时区时,我觉得困难是一件容易的事,但我越是深入研究这个话题,就越是迷失方向。 在Stack Overflow中,已经有几十个类似的问题,主要是试图解决特定的问题,但是没有一个(包括答案)帮助我理解我想要的主题。 除此之外,正如其他人所问,时不时地它看起来像是一些时区的变化,微软不得不通过Windows更新发布的更新来应对它。

    在我的例子中,我注意到至少有2个TZ是不正确的:

    • 阿尔泰标准时间
    • 阿根廷标准时间

    因为正如我上面提到的,TZ是从本地系统获取的,我试图搜索更新,但没有。所以要么我真的错过了一些重要的东西,要么微软不在乎这两个时区。

    有关DST/TZ更新的信息

    为了 Daylight Saving Time ,微软有明确的政策和规定:

    微软努力将这些更改合并到Windows中,并通过windowsupdate(WU)发布更新。通过WU发布的每个DST/TZ更新都将有最新的时间数据,并将取代之前发布的任何DST/TZ更新

    最近的更新可以在专用的 Microsoft Tech Community site

    为了帮助自己理解,我创建了一个简单的控制台应用程序(见下面的代码),问题是这还不够。

    示例中使用的最重要的类和方法的概述

            private static ConsoleColor DefaultColor;
        static void Main(string[] args)
        {
            DefaultColor = Console.ForegroundColor;
    
            foreach (var timeZoneInfo in TimeZoneInfo.GetSystemTimeZones().OrderBy(tz => tz.Id))
            {
                var firstQuart = new DateTime(2019, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                var secondQuart = new DateTime(2019, 4, 1, 0, 0, 0, DateTimeKind.Utc);
                var thirdQuart = new DateTime(2019, 7, 1, 0, 0, 0, DateTimeKind.Utc);
                var lastQuart = new DateTime(2019, 10, 1, 0, 0, 0, DateTimeKind.Utc);
    
                if (timeZoneInfo.Id == "Altai Standard Time" || 
                    timeZoneInfo.Id == "Argentina Standard Time" ||
                    timeZoneInfo.Id == "GMT Standard Time"
                    )
               {
                    Log($"{timeZoneInfo.DisplayName} (ID: {timeZoneInfo.Id})", ConsoleColor.Yellow);
                    Log($"StandardName: {timeZoneInfo.StandardName}");
                    Log($"DST: {timeZoneInfo.SupportsDaylightSavingTime}");
                    Log($"Daylight Name: {timeZoneInfo.DaylightName}");
                    Log();
                    Log($"UTC Offset: {timeZoneInfo.BaseUtcOffset}");
                    Log($"Dates for each quarter in this year");
    
                    var convertedFirstQuart = TimeZoneInfo.ConvertTimeFromUtc(firstQuart, timeZoneInfo);
                    var convertedSecondQuart = TimeZoneInfo.ConvertTimeFromUtc(secondQuart, timeZoneInfo);
                    var convertedThirdQuart = TimeZoneInfo.ConvertTimeFromUtc(thirdQuart, timeZoneInfo);
                    var convertedLastQuart = TimeZoneInfo.ConvertTimeFromUtc(lastQuart, timeZoneInfo);
    
                    Log();
                    Log($"First quarter: {TimeZoneInfo.ConvertTimeFromUtc(firstQuart, timeZoneInfo)}", ConsoleColor.Green);
                    Log($"DST (DateTime.IsDaylightSavingTime): {convertedFirstQuart.IsDaylightSavingTime()}");
                    Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedFirstQuart)}");
                    Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedFirstQuart)}/{timeZoneInfo.IsInvalidTime(convertedFirstQuart)}");
                    Log();
                    Log($"Second quarter: {TimeZoneInfo.ConvertTimeFromUtc(secondQuart, timeZoneInfo)}", ConsoleColor.Green);
                    Log($"DST (DateTime.IsDaylightSavingTime): {convertedSecondQuart.IsDaylightSavingTime()}");
                    Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedSecondQuart)}");
                    Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedSecondQuart)}/{timeZoneInfo.IsInvalidTime(convertedSecondQuart)}");
                    Log();
                    Log($"Third quarter: {TimeZoneInfo.ConvertTimeFromUtc(thirdQuart, timeZoneInfo)}", ConsoleColor.Green);
                    Log($"DST (DateTime.IsDaylightSavingTime): {convertedThirdQuart.IsDaylightSavingTime()}");
                    Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedThirdQuart)}");
                    Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedThirdQuart)}/{timeZoneInfo.IsInvalidTime(convertedThirdQuart)}");
                    Log();
                    Log($"Last quarter: {TimeZoneInfo.ConvertTimeFromUtc(lastQuart, timeZoneInfo)}", ConsoleColor.Green);
                    Log($"DST (DateTime.IsDaylightSavingTime): {convertedLastQuart.IsDaylightSavingTime()}");
                    Log($"DST (TimeInfo.IsDaylightSavingTime(DateTime): {timeZoneInfo.IsDaylightSavingTime(convertedLastQuart)}");
                    Log($"Ambiguous/Invalid: {timeZoneInfo.IsAmbiguousTime(convertedLastQuart)}/{timeZoneInfo.IsInvalidTime(convertedLastQuart)}");
                    Log("==============================================================");
                    Log();
                }
            }
    
            Console.ReadKey();
        }
    
        private static void Log(string message = "", ConsoleColor? color = null)
        {
            if(color.HasValue)
                Console.ForegroundColor = color.Value;
    
            Console.WriteLine(message);
            Console.ForegroundColor = DefaultColor;
        }
    }
    

    假设我的本地TZ是GMT,我们使用DST,输出如下:

    enter image description here enter image description here

    TimeZoneInfo.SupportsDaylightSavingTime支持() : Official Documentation

    下面的示例检索 在本地系统上可用,并显示 不支持夏令时。

    var zones = TimeZoneInfo.GetSystemTimeZones();
    foreach(TimeZoneInfo zone in zones)
    {
       if (! zone.SupportsDaylightSavingTime)
          Console.WriteLine(zone.DisplayName);
    }
    

    时区信息.IsDaylightSavingTime(日期时间) : Official Documentation

    指示指定的日期和时间是否在 当前时区信息时区的夏令时 对象。

    DateTime.IsDaylightSavingTime日期时间() : Official Documentation

    指示此DateTime实例是否在日光范围内 正在保存当前时区的时间范围。

    重要的是要明白(一开始我没有),这个方法 是日光节约时间吗 在…的实例上 日期时间 ,始终返回请求的信息,并考虑 本地系统时区 .

    分析输出

    专注于 阿根廷标准时间 ,我们可以看到 TimeZoneInfo.SupportsDaylightSavingTime支持 ,返回 是的 ,这是一个错误的信息,因为我到处搜索它,我发现相反的结果。

    即使是对DST的支持也似乎是不正确的,使用C#将UTC DateTime转换为ART TZ总是产生正确的结果。

    是什么让我觉得我还是不明白这里的全貌,是不是 TimeInfo.IsDaylightSavingTime时间(日期时间) 返回 ,这正是我所期望的。

    永久夏令时

    根据维基百科 https://en.wikipedia.org/wiki/Daylight_saving_time 有时提倡实行“永久性夏令时”(全年在夏季工作,无时间轮班),目前在阿根廷、白俄罗斯、[78]加拿大(如萨斯喀彻温省)、冰岛、吉尔吉斯斯坦、马来西亚、摩洛哥、纳米比亚、新加坡、土耳其、,土库曼斯坦和乌兹别克斯坦。[164]这可能是由于遵循邻近地区的时区、政治意愿或其他原因造成的。

    总之,我的开放性问题是:

    • 为什么是 TimezoneInfo.SupportsDaylightSavingTime支持() 返回 是的 但是 TimeInfo.IsDaylightSavingTime时间(日期时间) 返回 ?
    • 除了我上面解释的内容外,我如何确保我拥有来自Microsoft的最新DST/TZ更新?
    0 回复  |  直到 5 年前
        1
  •  2
  •   Matt Johnson-Pint    5 年前

    简短的回答

    TimeZoneInfo.SupportsDaylightSavingTime 考虑 全部的 系统上可用的时区数据,而不仅仅是当年的时区数据。两者 Argentina Standard Time Altai Standard Time 在Windows为其跟踪的时间段内,具有DST生效的时间段。


    更长的答案

    的文档 TimeZoneInfo.SupportsDaylightSavingTime支持 (您在问题中已链接到)解释:

    获取一个值,该值指示时区是否有任何夏令时规则。

    有点不太清楚的是,它是专门指 TimeZoneInfo.AdjustmentRule 对象,由 TimeZoneInfo.GetAdjustmentRules 方法,这些是 全部的 系统规则,而不仅仅是当年的规则。

    Microsoft策略声明Windows将跟踪2010年以后的所有更改。 但是,有些时区(例如阿根廷)在编写策略之前已经在跟踪更改,因此在某些情况下,您将看到早期的数据。

    在Windows注册表中,您可以在以下项中找到系统知道的所有时区数据:

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
    

    每个 TimeZoneInfo.Id 值对应于该子项下的子项,并且调整规则(如果有的话)将在下面 Dynamic DST 在那下面。

    为了 Argentina Standard Time\Dynamic DST ,我们找到了2006年到2010年的数据。

    image

    即使不解码这些数据,我们也能看到不同年份之间的差异。看着 timeanddate.com here 提供了详细信息:

    image

    在2007-08年和2008-09年的夏季,夏令时似乎已经生效。(阿根廷位于南半球,夏季分为两年。)

    事实上,我们可以从.NET中看到:

    var tz = TimeZoneInfo.FindSystemTimeZoneById("Argentina Standard Time");
    var dt = new DateTime(2008, 1, 1);
    var dst = tz.IsDaylightSavingTime(dt); // True
    

    因此,它 适合 TimeZoneInfo.SupportsDaylightSavingTime支持 返回 True 因为这个时区确实有一些有效的日期是在夏令时。

    阿尔泰也是如此。Windows正在跟踪2010年以后的数据 DST existed in 2010 there .

    image

    请注意,它的 标准时间 2014年和2016年。这是可能存在调整规则的另一个原因。不幸的是,如果不使用标记为“DST开始”或“DST结束”的转换,Windows就无法对此进行建模。因此,这些年的一部分会回来 是的 IsDaylightSavingTime ,即使过渡的任何一方都不被认为是夏令时。这是Windows上时区的已知问题。这是一个折衷方案,可以确保使用正确的UTC偏移量,即使转换是为了更改标准时间而不是夏令时。

    如果您确实需要知道转换是否与DST相关,那么您可以使用 IANA time zone data 相反,通过 Noda Time 图书馆。这个 ZoneInterval.Savings 物业会告诉你的。 然而 ,那就说到“永久夏令时”了。即使在IANA数据库中,这些通常也被视为对标准时间的更改,而不是真正的夏令时。所以,你可能会发现这样的情况,有人可能会说“我们已经在永久性夏令时多年”,但数据不同意。

    关于最后一个问题:

    除了我上面解释的内容外,我如何确保我拥有来自Microsoft的最新DST/TZ更新?

    只需确保您正在运行Windows Update即可。社区站点上列出的所有DST/TZ更新都与所有受支持的Windows版本的常规更新一起部署,而不管您处于哪个时区。