I'm working with a 3rd party API that returns Time of Day values as DateTime values filling in Jan 1, 1970 as the date part. So for 5AM, it will return something like 1969-12-31T21:03:00.000-08:00
The problem is that, when if the user was on London time, C# fails to apply BST adjustment for 1970-01-01.
For example, 1970-01-01 5AM
in UTC should be 1970-01-01 6AM
in London.
See conversion
But, C# doesn't seem to apply this conversion:
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);
The above code will still print 5AM.
However, if I set it to a more recent date that BST was effective such as Oct 1st, 2016, the same code works correctly printing 6AM.
When you use the TimeZoneInfo
class on Windows to work with a system time zone ("GMT Standard Time"
in this case), you are asking for time zone data from the Windows operating system, which is stored in the registry at:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
Windows simply does not carry historical data back that far. For "GMT Standard Time"
, which is the time zone in London that alternates between GMT and BST, it only knows about one set of rules - the current ones that are in effect. It does not know about anything before that (it last changed in 1996).
Note that per Microsoft's DST/TZ Support Policy, only changes from 2010 forward are guaranteed to be recorded. There are several older changes that have historically been in Windows (such as the US 2007 DST change and others), which do indeed work, but from a global perspective you may not get the best results with dates before 2010.
For that, you'll need a full implementation of the IANA TZ Database, and in .NET, one of the best ways to do that is with the Noda Time library.
Your code, transliterated to 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
Also note that if you are running .NET Core on Linux or OSX, then the time zones are indeed the IANA ones, thus your original code would work by just using the IANA identifer
var britishZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/London");
See also the timezone tag wiki.