为啥 C# 没有检测到 1970/1/1 在 BST 下?

Posted

技术标签:

【中文标题】为啥 C# 没有检测到 1970/1/1 在 BST 下?【英文标题】:Why doesn't C# detect that 1970/1/1 was under BST?为什么 C# 没有检测到 1970/1/1 在 BST 下? 【发布时间】:2017-04-11 17:18:13 【问题描述】:

我正在使用第 3 方 API,它将时间值作为 DateTime 值返回,并填写 1970 年 1 月 1 日作为日期部分。所以对于早上 5 点,它会返回类似1969-12-31T21:03:00.000-08:00

问题在于,如果用户在伦敦时间,C# 无法为 1970-01-01 应用 BST 调整。 例如,UTC 中的1970-01-01 5AM 应该是伦敦的1970-01-01 6AM。 See conversion

但是,C# 似乎没有应用这种转换:

var utcTime = new DateTime(1970, 1, 1, 5, 0, 0, DateTimeKind.Utc);
var britishZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var ukTime = TimeZoneInfo.ConvertTime(utcTime, britishZone);
Console.WriteLine(ukTime);

上面的代码仍然会打印 5AM。

但是,如果我将其设置为 BST 生效的较新日期,例如 2016 年 10 月 1 日,则相同的代码可以正确打印早上 6 点。

    为什么会这样? 如何解决这个问题?我们的应用需要跨任何时区工作(即硬编码时区不是一个选项 - 我们基本上使用 Windows 本地时区)。

【问题讨论】:

如果您可以使用第三方库,您可以查看Noda Time,这可能会更好。 谢谢。 Noda Time 的 DateTimeZone.GetUtcOffset(..) 方法确实返回 1970-1-1 的 +1 偏移量,而 .NET 的 TimeZoneInfo.GetUtcOffset(..) 没有。但是,转换为 Noda Time 对我们来说是最后的手段。 【参考方案1】:

当您在 Windows 上使用 TimeZoneInfo 类来处理系统时区(在本例中为 "GMT Standard Time")时,您正在请求来自 Windows 操作系统的时区数据,该数据存储在注册表中:

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

Windows 根本无法将历史数据带回那么远。对于"GMT Standard Time",这是在 GMT 和 BST 之间交替的伦敦时区,它只知道一组规则 - 当前有效的规则。在此之前它不知道任何事情(最后一次更改是在 1996 年)。

请注意,根据 Microsoft 的 DST/TZ Support Policy,保证只会记录 2010 年以后的更改。 Windows 历史上有几个较早的更改(例如美国 2007 年 DST 更改等)确实有效,但从全球角度来看,您可能无法在 2010 年之前获得最佳结果。

为此,您需要完整实现 the IANA TZ Database,而在 .NET 中,最好的方法之一是使用 Noda Time 库。

你的代码,音译为野田时间:

var utcTime = Instant.FromUtc(1970, 1, 1, 5, 0, 0);
var britishZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var ukTime = utcTime.InZone(britishZone);
Console.WriteLine(ukTime.ToDateTimeUnspecified()); // just for equivalent output

// Prints:  1/1/1970 6:00:00 AM

另请注意,如果您在 Linux 或 OSX 上运行 .NET Core,则时区确实是 IANA 时区,因此您的原始代码只需使用 IANA 标识符即可工作

var britishZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/London");

另见the timezone tag wiki。

【讨论】:

感谢您的全面回答。【参考方案2】:

您是否考虑过为有问题的历史时期创建自定义时区调整?

https://msdn.microsoft.com/en-us/library/bb397784(v=vs.110).aspx

可以在以下情况下使用:

时区没有关于特定历史时期时区调整的准确信息。

【讨论】:

【参考方案3】:

我认为代码运行正常。 BSt 从 3 月的最后一个星期日开始,直到 10 月的最后一个星期日,时钟向前移动一小时。那是UTC + 1。由于您正在查看 1 月的日期,因此伦敦时间将与 UTC 相同。即 utc+0 查看***了解时区详情https://en.m.wikipedia.org/wiki/British_Summer_Time

【讨论】:

非常感谢 Swetha 提供的链接。我认为它确实表明 C# 根据“...英国标准时间实验的行为不正确,英国全年保持在 GMT+1。这发生在 1968 年 10 月 27 日至 1971 年 10 月 31 日之间,当时有一个回归之前的安排。” 被否决 - 因为 1) 1970 年 1 月,伦敦确实是 GMT+1,而不是 GMT+0。 2) 代码在这段时间内无法正常工作,因为 Windows 不包含可以追溯到那么远的数据。

以上是关于为啥 C# 没有检测到 1970/1/1 在 BST 下?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 C# 的字符串中未检测到“空白”?

为啥 C# 提前 7 小时解析 DateTime?

为啥我的datetimepicker默认显示1970年1月1日

为啥刷新页面后没有检测到Vuex? (努努特)

C# 的BS和CS

为啥粉碎后没有立即出现“检测到堆栈粉碎”?