C# DateTime 无法识别时区更改 (BST)
C# DateTime not recognising timezone change (BST)
几周前,即 2016 年 3 月 27 日,英国的时钟拨快了一小时。在 01:00,时钟 'jumped' 前进到 02:00
http://www.timeanddate.com/time/dst/events.html
这意味着“2016 年 3 月 27 日,01:30”是无效的日期时间,即它不代表实际 'happened'.
的时间
最近,当 Java 日期时间解析器无法理解我们传递给它的时间时,这让我们陷入困境。但是,C# DateTime 似乎根本没有任何问题。
DateTime dt = DateTime.Parse("2016-03-27 01:30:00");
bool invalid = TimeZoneInfo.Local.IsInvalidTime(dt);
虽然 invalid
正确设置为 true
,但 DateTime
似乎没有任何存储问题。
此外,声明如下:
diff = new DateTime(2016, 3, 27, 2, 30, 0) - new DateTime(2016, 3, 27, 0, 30, 0);
导致 TimeSpan 代表 2 小时,这是不正确的。
为什么 DateTime 类型不考虑 DST?
DateTime 有一个 Kind 属性,指示它是本地时间还是 UTC 或未指定。您的解析格式不允许它设置这种类型,因此它将是未指定的。我认为您需要对其进行设置以使其正确处理夏令时。
一般都是处理UTC格式的DateTimes,然后在需要显示给用户的时候转为本地DateTime来解决这个问题。
我想 Microsoft 决定为您提供灵活性,让您可以使用 IsInvalidTime 方法自行管理无效日期。
如果您的应用程序要求日期有效,那么您应该检查时间是否有效。
所以,这里唯一的问题是:
Why does the DateTime type not account for DST?
简答
因为 DateTime
结构就是这样设计的。
来自 the MSDN docs:
Conversion operations between time zones (such as between UTC and local time, or between one time zone and another) take daylight saving time into account, but arithmetic and comparison operations do not.
更长的答案
.NET 的DateTime
只跟踪两个逻辑值,Ticks
和Kind
。在内部,出于性能和兼容性原因,它们被合并为一个 64 位整数,但您可以将它们视为两个独立的值。
Ticks
跟踪自纪元值 0001-01-01 00:00:00
.
以来 100 纳秒间隔的数量
Kind
跟踪 Ticks
是否旨在表示 UTC 时间、计算机本地时区时间或其他未指定时区时间的元数据。
每个使用 DateTime
的函数都应该考虑 Kind
。框架中的许多人都这样做。有些没有。许多其他图书馆,甚至一些与时间有关的图书馆,都完全忽略它。
当您调用 DateTime.Parse("2016-03-27 01:30:00")
时,Kind
设置为 Unspecified
,因为没有上下文。您提供的值不一定在任何特定时区,因此 0001-01-01 00:00:00
纪元日期也不是。换句话说,您可以将其视为未固定到 UTC 或任何 DST 规则的任何偏移量的日期和时间。
请注意,这与 Java 和 JavaScript 有很大不同,其中 Date
对象跟踪自 1970-01-01 00:00:00 UTC
纪元以来的毫秒数。 总是 UTC,而 .NET 可能是也可能不是。
有多种方法可以解决这个问题。目前最好的是使用更明智的 API,它由 Noda Time 开源库提供。但是,如果您只想使用 .NET 中内置的功能,请考虑使用 DateTimeOffset
结构。与 DateTime
不同,它跟踪其与 UTC 的偏移量并在计算时将其考虑在内。
您仍然需要使用 TimeZoneInfo.IsInvalidTime
检查输入是否无效。 .NET 中没有内置 API 会在 解析 时抛出无效时间异常。但是你会发现,TimeZoneInfo
上的转换函数,比如ConvertTimeToUtc
,如果你传入一个源时区的无效时间,确实会抛出异常。
您还应该检查 TimeZoneInfo.IsAmbiguousTime
,因为在出现两次的回退转换期间给定一个值,例如英国的 2016-10-30 01:30
,.NET 将始终选择 标准 时间偏移量。虽然乍一看这似乎是正确的,但请考虑 daylight 实例实际上首先发生 - 这通常是大多数现实世界场景中所需的行为(无论如何根据我的经验)。
此外,关于您的 Java 代码,如果您还没有,您应该考虑使用 Joda Time for Java 7 及更低版本,或者java.time
内置于 Java 8。您会发现它们与前面提到的 Noda Time 非常相似。
最后,我将添加我非常固执己见的断言,即 DateTime
结构的设计在多个方面违反了 SRP。特别是,DateTimeKind
是可憎的 - 恕我直言。
我知道这已经过时了,但我偶然发现了它,并认为添加到线程中可能会对其他人有所帮助。虽然 Matt Johnson-Pint 给出的答案非常全面且确实非常有帮助,但有一种简单的方法可用于实现 DateTime 所需的 'tweak' 以显示当前当地时间 - 我认为这回答了实际问题:
string dtNow = DateTime.Now.ToLocalTime().ToString();
在英国 BST 中,这给出了正确的结果。
有用linkhere
Azure Web 应用程序有一个很好的默认设置,可用于在 Azure 界面或 Visual Studio 中更改此设置。
这里有一个很棒的博客向您展示了如何做到这一点:
https://www.jasongaylord.com/blog/tip-changing-an-azure-app-service-time-zone
几周前,即 2016 年 3 月 27 日,英国的时钟拨快了一小时。在 01:00,时钟 'jumped' 前进到 02:00
http://www.timeanddate.com/time/dst/events.html
这意味着“2016 年 3 月 27 日,01:30”是无效的日期时间,即它不代表实际 'happened'.
的时间最近,当 Java 日期时间解析器无法理解我们传递给它的时间时,这让我们陷入困境。但是,C# DateTime 似乎根本没有任何问题。
DateTime dt = DateTime.Parse("2016-03-27 01:30:00");
bool invalid = TimeZoneInfo.Local.IsInvalidTime(dt);
虽然 invalid
正确设置为 true
,但 DateTime
似乎没有任何存储问题。
此外,声明如下:
diff = new DateTime(2016, 3, 27, 2, 30, 0) - new DateTime(2016, 3, 27, 0, 30, 0);
导致 TimeSpan 代表 2 小时,这是不正确的。
为什么 DateTime 类型不考虑 DST?
DateTime 有一个 Kind 属性,指示它是本地时间还是 UTC 或未指定。您的解析格式不允许它设置这种类型,因此它将是未指定的。我认为您需要对其进行设置以使其正确处理夏令时。
一般都是处理UTC格式的DateTimes,然后在需要显示给用户的时候转为本地DateTime来解决这个问题。
我想 Microsoft 决定为您提供灵活性,让您可以使用 IsInvalidTime 方法自行管理无效日期。
如果您的应用程序要求日期有效,那么您应该检查时间是否有效。
所以,这里唯一的问题是:
Why does the DateTime type not account for DST?
简答
因为 DateTime
结构就是这样设计的。
来自 the MSDN docs:
Conversion operations between time zones (such as between UTC and local time, or between one time zone and another) take daylight saving time into account, but arithmetic and comparison operations do not.
更长的答案
.NET 的DateTime
只跟踪两个逻辑值,Ticks
和Kind
。在内部,出于性能和兼容性原因,它们被合并为一个 64 位整数,但您可以将它们视为两个独立的值。
Ticks
跟踪自纪元值0001-01-01 00:00:00
. 以来 100 纳秒间隔的数量
Kind
跟踪Ticks
是否旨在表示 UTC 时间、计算机本地时区时间或其他未指定时区时间的元数据。
每个使用 DateTime
的函数都应该考虑 Kind
。框架中的许多人都这样做。有些没有。许多其他图书馆,甚至一些与时间有关的图书馆,都完全忽略它。
当您调用 DateTime.Parse("2016-03-27 01:30:00")
时,Kind
设置为 Unspecified
,因为没有上下文。您提供的值不一定在任何特定时区,因此 0001-01-01 00:00:00
纪元日期也不是。换句话说,您可以将其视为未固定到 UTC 或任何 DST 规则的任何偏移量的日期和时间。
请注意,这与 Java 和 JavaScript 有很大不同,其中 Date
对象跟踪自 1970-01-01 00:00:00 UTC
纪元以来的毫秒数。 总是 UTC,而 .NET 可能是也可能不是。
有多种方法可以解决这个问题。目前最好的是使用更明智的 API,它由 Noda Time 开源库提供。但是,如果您只想使用 .NET 中内置的功能,请考虑使用 DateTimeOffset
结构。与 DateTime
不同,它跟踪其与 UTC 的偏移量并在计算时将其考虑在内。
您仍然需要使用 TimeZoneInfo.IsInvalidTime
检查输入是否无效。 .NET 中没有内置 API 会在 解析 时抛出无效时间异常。但是你会发现,TimeZoneInfo
上的转换函数,比如ConvertTimeToUtc
,如果你传入一个源时区的无效时间,确实会抛出异常。
您还应该检查 TimeZoneInfo.IsAmbiguousTime
,因为在出现两次的回退转换期间给定一个值,例如英国的 2016-10-30 01:30
,.NET 将始终选择 标准 时间偏移量。虽然乍一看这似乎是正确的,但请考虑 daylight 实例实际上首先发生 - 这通常是大多数现实世界场景中所需的行为(无论如何根据我的经验)。
此外,关于您的 Java 代码,如果您还没有,您应该考虑使用 Joda Time for Java 7 及更低版本,或者java.time
内置于 Java 8。您会发现它们与前面提到的 Noda Time 非常相似。
最后,我将添加我非常固执己见的断言,即 DateTime
结构的设计在多个方面违反了 SRP。特别是,DateTimeKind
是可憎的 - 恕我直言。
我知道这已经过时了,但我偶然发现了它,并认为添加到线程中可能会对其他人有所帮助。虽然 Matt Johnson-Pint 给出的答案非常全面且确实非常有帮助,但有一种简单的方法可用于实现 DateTime 所需的 'tweak' 以显示当前当地时间 - 我认为这回答了实际问题:
string dtNow = DateTime.Now.ToLocalTime().ToString();
在英国 BST 中,这给出了正确的结果。
有用linkhere
Azure Web 应用程序有一个很好的默认设置,可用于在 Azure 界面或 Visual Studio 中更改此设置。 这里有一个很棒的博客向您展示了如何做到这一点: https://www.jasongaylord.com/blog/tip-changing-an-azure-app-service-time-zone