TimeZoneInfo SupportsDaylightSavingTime 不符合我的预期 return,为什么?
TimeZoneInfo SupportsDaylightSavingTime does not return what I expect, why?
当我开始使用 Time Zones 时,我的任务很简单,但我越深入这个话题,就越发现自己迷失了方向。
在 Stack Overflow 中已经有几十个类似的问题,主要是试图解决特定问题,但其中 none 个(包括答案)帮助我按照自己的意愿理解了主题。
最重要的是,正如其他人所问,时区似乎有时会发生变化,Microsoft 不得不通过 Windows Update 快速分发更新以应对它。
就我而言,我注意到至少有 2 个 TZ 不正确:
- 阿尔泰标准时间
- 阿根廷标准时间
因为正如我上面提到的,TZ 是从我试图搜索更新的本地系统中获取的,但我有 none。所以要么我真的遗漏了一些重要的东西,要么微软不关心这两个时区。
关于 DST/TZ 更新的信息
对于 Daylight Saving Time,Microsoft 有明确的政策并声明:
Microsoft makes an effort to incorporate these changes to Windows, and publishes an update through Windows Update (WU). Each DST/TZ update released through WU will have the latest time data and will also supersede any previously issued DST/TZ update
最近的更新可以在专用的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,输出如下:
TimeZoneInfo.SupportsDaylightSavingTime(): Official Documentation
The following example retrieves a collection of all time zones that
are available on a local system and displays the names of those that
do not support daylight saving time.
var zones = TimeZoneInfo.GetSystemTimeZones();
foreach(TimeZoneInfo zone in zones)
{
if (! zone.SupportsDaylightSavingTime)
Console.WriteLine(zone.DisplayName);
}
TimezoneInfo.IsDaylightSavingTime(日期时间):Official Documentation
Indicates whether a specified date and time falls in the range of
daylight saving time for the time zone of the current TimeZoneInfo
object.
DateTime.IsDaylightSavingTime(): Official Documentation
Indicates whether this instance of DateTime is within the daylight
saving time range for the current time zone.
重要的是要了解(一开始我没有),IsDaylightSavingTime 方法在 DateTime 的实例上,总是returns 请求的信息考虑了 本地系统时区 。
分析输出
关注阿根廷标准时间,我们可以看到 TimeZoneInfo.SupportsDaylightSavingTime, return true,这是一个错误的信息,因为我到处搜索它,我发现相反的结果。
即使对 DST 的支持似乎不正确,使用 C# 将 UTC DateTime 转换为 ART TZ 也总是会产生正确的结果。
是什么让我觉得我仍然不明白这里的全貌,是 TimeInfo.IsDaylightSavingTime(DateTime) return false,这是我所期望的。
永久夏令时
根据维基百科https://en.wikipedia.org/wiki/Daylight_saving_time
有时提倡转向"permanent daylight saving time"(全年保持夏令时,没有时差),目前在阿根廷、白俄罗斯、[78]加拿大(例如萨斯喀彻温省)、冰岛、吉尔吉斯斯坦、马来西亚等一些司法管辖区实施、摩洛哥、纳米比亚、新加坡、土耳其、土库曼斯坦和乌兹别克斯坦。[164]这可能是遵循邻近地区时区、政治意愿或其他原因的结果。
总而言之,我的未决问题是:
- 为什么 TimezoneInfo.SupportsDaylightSavingTime() return true 但 TimeInfo.IsDaylightSavingTime(日期时间) return false?
- 除了我上面解释的内容之外,我如何确保我拥有来自 Microsoft 的最新 DST/TZ 更新?
简答
TimeZoneInfo.SupportsDaylightSavingTime
考虑系统上可用的 所有 时区数据,而不仅仅是当年的时区数据。 Argentina Standard Time
和 Altai Standard Time
都有 DST 生效的时间段,在 Windows 跟踪它们的时间段内。
更长的答案
TimeZoneInfo.SupportsDaylightSavingTime
的文档(您已经在问题中链接到)解释了:
Gets a value indicating whether the time zone has any daylight saving time rules.
不太清楚的是,它具体指的是 TimeZoneInfo.AdjustmentRule
对象,如 return 由 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 年的数据。
即使不对数据进行解码,我们也可以看出年份之间存在差异。查看 timeanddate.com here 可以得到详细信息:
看来夏令时在 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
到return True
,因为这次确实有有效日期在夏令时区域。
阿尔泰也是如此。 Windows 是从 2010 年开始的跟踪数据,DST existed in 2010 there.
请注意,它在 2014 年和 2016 年的 标准时间 也有变化。这是可能存在调整规则的另一个原因。不幸的是,Windows 无法在不使用标记为 "start of DST" 或 "end of DST" 的转换的情况下对其进行建模。因此,这些年份的某些部分将从 IsDaylightSavingTime
return True
,即使过渡的任何一方都不被认为是夏令时。这是 Windows 时区的一个已知问题。这是一种折衷方案,可确保使用正确的 UTC 偏移量,即使转换是为了更改为标准时间而不是夏令时。
如果您绝对需要知道转换是否与 DST 相关,那么您可以使用 IANA time zone data instead, via the Noda Time 库。 ZoneInterval.Savings
属性 会告诉你。 但是,这就是您关于 "permanent daylight saving time" 的观点。即使在 IANA 数据库中,这些通常也被视为对标准时间的更改,而不是真正的夏令时。因此,您可能会发现有人可能会说 "We've been on permanent DST for years",但数据不同意的情况。
关于你的最后一个问题:
How can I make sure that I have the latest DST/TZ updates from Microsoft, beside what I explained above?
只需确保您正在 运行 Windows 更新就足够了。社区站点上列出的所有 DST/TZ 更新都与 Windows 所有受支持版本的定期更新一起部署,无论您身处哪个时区。
当我开始使用 Time Zones 时,我的任务很简单,但我越深入这个话题,就越发现自己迷失了方向。 在 Stack Overflow 中已经有几十个类似的问题,主要是试图解决特定问题,但其中 none 个(包括答案)帮助我按照自己的意愿理解了主题。 最重要的是,正如其他人所问,时区似乎有时会发生变化,Microsoft 不得不通过 Windows Update 快速分发更新以应对它。
就我而言,我注意到至少有 2 个 TZ 不正确:
- 阿尔泰标准时间
- 阿根廷标准时间
因为正如我上面提到的,TZ 是从我试图搜索更新的本地系统中获取的,但我有 none。所以要么我真的遗漏了一些重要的东西,要么微软不关心这两个时区。
关于 DST/TZ 更新的信息
对于 Daylight Saving Time,Microsoft 有明确的政策并声明:
Microsoft makes an effort to incorporate these changes to Windows, and publishes an update through Windows Update (WU). Each DST/TZ update released through WU will have the latest time data and will also supersede any previously issued DST/TZ update
最近的更新可以在专用的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,输出如下:
TimeZoneInfo.SupportsDaylightSavingTime(): Official Documentation
The following example retrieves a collection of all time zones that are available on a local system and displays the names of those that do not support daylight saving time.
var zones = TimeZoneInfo.GetSystemTimeZones();
foreach(TimeZoneInfo zone in zones)
{
if (! zone.SupportsDaylightSavingTime)
Console.WriteLine(zone.DisplayName);
}
TimezoneInfo.IsDaylightSavingTime(日期时间):Official Documentation
Indicates whether a specified date and time falls in the range of daylight saving time for the time zone of the current TimeZoneInfo object.
DateTime.IsDaylightSavingTime(): Official Documentation
Indicates whether this instance of DateTime is within the daylight saving time range for the current time zone.
重要的是要了解(一开始我没有),IsDaylightSavingTime 方法在 DateTime 的实例上,总是returns 请求的信息考虑了 本地系统时区 。
分析输出
关注阿根廷标准时间,我们可以看到 TimeZoneInfo.SupportsDaylightSavingTime, return true,这是一个错误的信息,因为我到处搜索它,我发现相反的结果。
即使对 DST 的支持似乎不正确,使用 C# 将 UTC DateTime 转换为 ART TZ 也总是会产生正确的结果。
是什么让我觉得我仍然不明白这里的全貌,是 TimeInfo.IsDaylightSavingTime(DateTime) return false,这是我所期望的。
永久夏令时
根据维基百科https://en.wikipedia.org/wiki/Daylight_saving_time 有时提倡转向"permanent daylight saving time"(全年保持夏令时,没有时差),目前在阿根廷、白俄罗斯、[78]加拿大(例如萨斯喀彻温省)、冰岛、吉尔吉斯斯坦、马来西亚等一些司法管辖区实施、摩洛哥、纳米比亚、新加坡、土耳其、土库曼斯坦和乌兹别克斯坦。[164]这可能是遵循邻近地区时区、政治意愿或其他原因的结果。
总而言之,我的未决问题是:
- 为什么 TimezoneInfo.SupportsDaylightSavingTime() return true 但 TimeInfo.IsDaylightSavingTime(日期时间) return false?
- 除了我上面解释的内容之外,我如何确保我拥有来自 Microsoft 的最新 DST/TZ 更新?
简答
TimeZoneInfo.SupportsDaylightSavingTime
考虑系统上可用的 所有 时区数据,而不仅仅是当年的时区数据。 Argentina Standard Time
和 Altai Standard Time
都有 DST 生效的时间段,在 Windows 跟踪它们的时间段内。
更长的答案
TimeZoneInfo.SupportsDaylightSavingTime
的文档(您已经在问题中链接到)解释了:
Gets a value indicating whether the time zone has any daylight saving time rules.
不太清楚的是,它具体指的是 TimeZoneInfo.AdjustmentRule
对象,如 return 由 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 年的数据。
即使不对数据进行解码,我们也可以看出年份之间存在差异。查看 timeanddate.com here 可以得到详细信息:
看来夏令时在 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
到return True
,因为这次确实有有效日期在夏令时区域。
阿尔泰也是如此。 Windows 是从 2010 年开始的跟踪数据,DST existed in 2010 there.
请注意,它在 2014 年和 2016 年的 标准时间 也有变化。这是可能存在调整规则的另一个原因。不幸的是,Windows 无法在不使用标记为 "start of DST" 或 "end of DST" 的转换的情况下对其进行建模。因此,这些年份的某些部分将从 IsDaylightSavingTime
return True
,即使过渡的任何一方都不被认为是夏令时。这是 Windows 时区的一个已知问题。这是一种折衷方案,可确保使用正确的 UTC 偏移量,即使转换是为了更改为标准时间而不是夏令时。
如果您绝对需要知道转换是否与 DST 相关,那么您可以使用 IANA time zone data instead, via the Noda Time 库。 ZoneInterval.Savings
属性 会告诉你。 但是,这就是您关于 "permanent daylight saving time" 的观点。即使在 IANA 数据库中,这些通常也被视为对标准时间的更改,而不是真正的夏令时。因此,您可能会发现有人可能会说 "We've been on permanent DST for years",但数据不同意的情况。
关于你的最后一个问题:
How can I make sure that I have the latest DST/TZ updates from Microsoft, beside what I explained above?
只需确保您正在 运行 Windows 更新就足够了。社区站点上列出的所有 DST/TZ 更新都与 Windows 所有受支持版本的定期更新一起部署,无论您身处哪个时区。