TimeZoneInfo SupportsDaylightSavingTime不会返回我的期望,为什么?

问题描述 投票:2回答:1

当我开始使用Time Zones时,我很难做到这一点很简单,但是我越是沉浸在这个主题中,我就越发现自己迷失了。在Stack Overflow中已经有几十个类似的问题,主要是试图解决特定的问题,但没有一个(包括答案​​)帮助我理解我想要的主题。最重要的是,正如其他人所要求的那样,有时会看到一些时区变化,而微软必须急于通过Windows Update分发更新以应对它。

在我的情况下,我注意到至少2个TZ是不正确的:

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

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

Information about DST/TZ updates

对于Daylight Saving Time,微软有一个明确的政策并指出:

Microsoft努力将这些更改合并到Windows,并通过Windows Update(WU)发布更新。通过WU发布的每个DST / TZ更新都将包含最新的时间数据,并且还将取代之前发布的任何DST / TZ更新

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

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

Overview of the most important classes and methods used in the example

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

TimezoneInfo.IsDaylightSavingTime(DateTime):Official Documentation

指示指定的日期和时间是否落在当前TimeZoneInfo对象的时区的夏令时范围内。

DateTime.IsDaylightSavingTime():Official Documentation

指示此DateTime实例是否在当前时区的夏令时范围内。

重要的是要理解(我最初没有),在DateTime实例上的方法IsDaylightSavingTime总是返回考虑本地系统时区所请求的信息。

Analyse the output

关注阿根廷标准时间,我们可以看到TimeZoneInfo.SupportsDaylightSavingTime,返回true,这是一个不正确的信息,因为在我搜索它的任何地方,我发现了相反的结果。

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

是什么让我觉得我仍然不明白这里的全貌,是TimeInfo.IsDaylightSavingTime(DateTime)返回false,这是我所期望的。

Permanent daylight saving time

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

So to summarize, my open questions are:

  • 为什么TimezoneInfo.SupportsDaylightSavingTime()返回true但TimeInfo.IsDaylightSavingTime(DateTime)返回false?
  • 除了上面解释的内容之外,我如何确保从微软获得最新的DST / TZ更新?
c# datetime timezone dst
1个回答
2
投票

简答

TimeZoneInfo.SupportsDaylightSavingTime考虑系统上可用的所有时区数据,而不仅仅是当年的时区数据。在Windows跟踪它们的时间段内,Argentina Standard TimeAltai Standard Time都有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是合适的,因为这个时区的DST确实存在有效日期。

阿尔泰也是如此。 Windows正在跟踪2010年前进的数据和DST existed in 2010 there

image

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

如果您绝对需要知道转换是否与DST相关,那么您可以通过IANA time zone data库使用Noda TimeZoneInterval.Savings财产将告诉你。但是,这就达到了“永久夏令时”的意义。即使在IANA数据库中,这些通常也被视为标准时间的更改,而不是真正的夏令时。因此,您可能会发现有人可能会说“我们已经使用DST多年”的案例,但数据不一致。

关于你的上一个问题:

除了上面解释的内容之外,我如何确保从微软获得最新的DST / TZ更新?

只需确保运行Windows Update即可。无论您所在的时区如何,社区站点上列出的所有DST / TZ更新都会定期更新到所有受支持的Windows版本。

© www.soinside.com 2019 - 2024. All rights reserved.