简体   繁体   English

C# DateTime 无法识别时区更改 (BST)

[英]C# DateTime not recognising timezone change (BST)

A few weeks ago, on 27th March 2016, the clocks in the UK went forward by one hour.几周前,也就是 2016 年 3 月 27 日,英国的时钟快了一个小时。 At 01:00, the clocks 'jumped' forward to 02:00在 01:00,时钟“跳”到 02:00

http://www.timeanddate.com/time/dst/events.html http://www.timeanddate.com/time/dst/events.html

This means that "27 Mar 2016, 01:30", is an invalid datetime, ie it does not represent a time that actually 'happened'.这意味着“27 Mar 2016, 01:30”是无效的日期时间,即它不代表实际“发生”的时间。

This recently caught us out when a Java date time parser was unable to understand the time we passed to it.最近,当 Java 日期时间解析器无法理解我们传递给它的时间时,这让我们感到震惊。 However, the C# DateTime doesn't appear to have any problem with it at all.但是,C# DateTime 似乎根本没有任何问题。

DateTime dt = DateTime.Parse("2016-03-27 01:30:00"); 
bool invalid = TimeZoneInfo.Local.IsInvalidTime(dt);

Although invalid is correctly set to true , DateTime doesn't appear to have any problem storing it.尽管invalid已正确设置为true ,但DateTime存储它似乎没有任何问题。

Moreover, the following statement:此外,还有以下声明:

diff = new DateTime(2016, 3, 27, 2, 30, 0) - new DateTime(2016, 3, 27, 0, 30, 0);

results in a TimeSpan representing 2 hours, which is not correct.导致代表 2 小时的 TimeSpan,这是不正确的。

Why does the DateTime type not account for DST?为什么 DateTime 类型不考虑 DST?

So, the only question here is:所以,这里唯一的问题是:

Why does the DateTime type not account for DST?为什么 DateTime 类型不考虑 DST?

Short Answer简答

Because that's how the DateTime structure was designed.因为这就是DateTime结构的设计方式。

From the MSDN docs :MSDN 文档

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.时区之间的转换操作(例如 UTC 和本地时间之间,或一个时区和另一个时区之间的转换操作)会考虑夏令时,但算术和比较操作不会。

Longer Answer更长的答案

.NET's DateTime only tracks two logical values, Ticks and Kind . .NET 的DateTime只跟踪两个逻辑值, TicksKind Internally, these are merged into a single 64-bit integer for performance and compatibility reasons, but you can think of them as two separate values.在内部,出于性能和兼容性原因,这些被合并为一个 64 位整数,但您可以将它们视为两个单独的值。

  • Ticks tracks the number of 100 nanosecond intervals since an epoch value of 0001-01-01 00:00:00 . Ticks跟踪自纪元值为0001-01-01 00:00:00以来的 100 纳秒间隔数。

  • Kind tracks metadata of whether the Ticks are intended to represent time in UTC, or in the computer's local time zone, or in some other unspecified time zone. Kind跟踪元数据,了解Ticks是否打算以 UTC、计算机本地时区或其他未指定时区表示时间。

Every function that works with a DateTime is supposed to take Kind into consideration.每个与DateTime一起使用的函数都应该考虑Kind Many in the framework do.框架中的许多人都这样做。 Some do not.有些没有。 Many other libraries, even some concerned with time, ignore it altogether.许多其他图书馆,甚至一些与时间有关的图书馆,完全忽略了它。

When you called DateTime.Parse("2016-03-27 01:30:00") , the Kind is set to Unspecified , because there's no context.当您调用DateTime.Parse("2016-03-27 01:30:00")Kind设置为Unspecified ,因为没有上下文。 The value you provided isn't necessarily in any particular time zone, and thus neither is the 0001-01-01 00:00:00 epoch date.您提供的值不一定在任何特定时区,因此0001-01-01 00:00:00纪元日期也不是。 In other words, you can think of it as just a date and a time that are not pinned to any offset from UTC or any DST rules.换句话说,您可以将其视为与 UTC 或任何 DST 规则没有任何偏移的日期和时间。

Note this is considerably different from Java and JavaScript, where a Date object which tracks milliseconds since the 1970-01-01 00:00:00 UTC epoch.请注意,这与 Java 和 JavaScript 有很大不同,其中Date对象跟踪自1970-01-01 00:00:00 UTC纪元以来的毫秒数。 It's always UTC, where .NET's may or may not be.始终是UTC,其中 .NET 可能是也可能不是。

There are multiple ways to handle this.有多种方法可以处理这个问题。 The best by far is to use a more sensible API, which is offered by the Noda Time open source library.目前最好的是使用更合理的 API,它由Noda Time开源库提供。 However, if you want to use only the functionality built in to .NET, then consider using the DateTimeOffset structure.但是,如果您只想使用 .NET 内置的功能,请考虑使用DateTimeOffset结构。 Unlike DateTime , it tracks its offset from UTC and takes it into account when doing math.DateTime不同,它跟踪其与 UTC 的偏移量,并在进行数学运算时将其考虑在内。

You will still have to check if the input is invalid with TimeZoneInfo.IsInvalidTime .您仍然需要使用TimeZoneInfo.IsInvalidTime检查输入是否无效。 There is no API built in to .NET that will throw an exception for an invalid time when parsing . .NET 中没有内置 API 会在解析.NET 时在无效时间抛出异常。 You will, however, find that the conversion functions on TimeZoneInfo , such as ConvertTimeToUtc will indeed throw an exception if you pass in an invalid time for the source time zone.但是,您会发现TimeZoneInfo上的转换函数(例如ConvertTimeToUtc确实会在您为源时区传入无效时间时引发异常。

You should also check TimeZoneInfo.IsAmbiguousTime , because given a value during the fall-back transition that occurs twice, such as 2016-10-30 01:30 for the UK, .NET will always pick the standard time offset.您还应该检查TimeZoneInfo.IsAmbiguousTime ,因为在发生两次的回退转换期间给定一个值,例如英国的2016-10-30 01:30 ,.NET 将始终选择标准时间偏移。 While this may at first seem correct, consider that the daylight instance actually occurs first - which is usually the desired behavior in most real world scenarios (from my experiences anyway).虽然这乍一看似乎是正确的,但考虑到日光实例实际上首先发生 - 这通常是大多数现实世界场景中所需的行为(无论如何,根据我的经验)。

Aside, regarding your Java code, if you're not already you should consider using either Joda Time for Java 7 and lower, or java.time that's built in to Java 8. You'll find them very similar to Noda Time , mentioned earlier.另外,关于您的 Java 代码,如果您还没有使用 Java 7 及更低版本的Joda Time ,或者 Java 8 中内置的java.time 。您会发现它们与前面提到的Noda Time非常相似.

And lastly, I'll add my very opinionated assertion that the design of the DateTime structure violates SRP in multiple ways.最后,我将添加我非常固执的断言,即DateTime结构的设计以多种方式违反了SRP In particular, DateTimeKind is an abomination - IMHO.特别是, DateTimeKind是一个令人憎恶的东西——恕我直言。

I know this is old but I came across it and thought others might be helped by this addition to the thread.我知道这是旧的,但我遇到了它,并认为其他人可能会通过添加到线程中而有所帮助。 Whilst the answer given by Matt Johnson-Pint is comprehensive and very helpful indeed, there is a simple method available to achieve the required 'tweak' to DateTime in order to show current local time - which I think answers the practical point:虽然 Matt Johnson-Pint 给出的答案是全面且非常有帮助的,但有一种简单的方法可以实现对 DateTime 所需的“调整”以显示当前本地时间 - 我认为这回答了实际问题:

    string dtNow = DateTime.Now.ToLocalTime().ToString();

In the UK BST this gives the correct result.在英国 BST 中,这给出了正确的结果。

useful link here有用的链接在这里

DateTime has a Kind property which indicates if it is Local time or UTC or Unspecified. DateTime 有一个 Kind 属性,指示它是本地时间、UTC 还是未指定。 Your parsing format doesn't allow it to set this Kind so it will be Unspecified.您的解析格式不允许它设置这种类型,因此它将是未指定的。 I think you need to set this to allow it correctly deal with DST.我认为您需要设置它以使其正确处理 DST。

Generally you deal with DateTimes in UTC format, and then convert to local DateTime when you need to display to the user to get around this issue.一般你处理UTC格式的DateTimes,然后当你需要显示给用户时转换为本地DateTime来解决这个问题。

I guess Microsoft decided to supply you with the flexibility to manage the invalid dates yourself with the IsInvalidTime method.我猜微软决定为您提供使用 IsInvalidTime 方法自行管理无效日期的灵活性。

If your application has a requirement for the date to be valid then you should be checking if the time is valid.如果您的申请要求日期有效,那么您应该检查时间是否有效。

Azure Web Apps have a nice default setting that can be used to change this either in the Azure interface or Visual Studio. Azure Web Apps 有一个很好的默认设置,可用于在 Azure 界面或 Visual Studio 中更改此设置。 There is a great blog here that shows you how to do it: https://www.jasongaylord.com/blog/tip-changing-an-azure-app-service-time-zone这里有一个很棒的博客,向您展示了如何操作: https : //www.jasongaylord.com/blog/tip-changed-an-azure-app-service-time-zone

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM