简体   繁体   中英

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. At 01:00, the clocks 'jumped' forward to 02:00

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'.

This recently caught us out when a Java date time parser was unable to understand the time we passed to it. However, the C# DateTime doesn't appear to have any problem with it at all.

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.

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.

Why does the DateTime type not account for DST?

So, the only question here is:

Why does the DateTime type not account for DST?

Short Answer

Because that's how the DateTime structure was designed.

From 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.

Longer Answer

.NET's DateTime only tracks two logical values, Ticks and Kind . 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.

  • Ticks tracks the number of 100 nanosecond intervals since an epoch value of 0001-01-01 00:00:00 .

  • 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.

Every function that works with a DateTime is supposed to take Kind into consideration. 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. 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. 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.

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. It's always UTC, where .NET's may or may not be.

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. However, if you want to use only the functionality built in to .NET, then consider using the DateTimeOffset structure. Unlike DateTime , it tracks its offset from UTC and takes it into account when doing math.

You will still have to check if the input is invalid with TimeZoneInfo.IsInvalidTime . There is no API built in to .NET that will throw an exception for an invalid time when parsing . 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.

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. 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.

And lastly, I'll add my very opinionated assertion that the design of the DateTime structure violates SRP in multiple ways. In particular, DateTimeKind is an abomination - IMHO.

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:

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

In the UK BST this gives the correct result.

useful link here

DateTime has a Kind property which indicates if it is Local time or UTC or Unspecified. 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.

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.

I guess Microsoft decided to supply you with the flexibility to manage the invalid dates yourself with the IsInvalidTime method.

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. 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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