Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DateTime.ToLocalTime() behavior change with .NET 4.5 on Server 2008R2?

Tags:

c#

.net

datetime

We are seeing bizarre behavior with .NET 4.5 and System.DateTime. The behavior of ToLocalTime() when applied to DateTime objects with Kind=Utc seems different on Server 2008R2 machines with .NET 4.5 compared with .NET 4.0. Even more bizarre, the problem does not manifest on developer PCs with .NET 4.5 installed.

Does anyone have an explanation for this behavior? I can't turn up any bug reports on Microsoft sites. We can use a more complicated approach to convert times that works, but it's hard to ensure no one ever uses .ToLocalTime() in the future.

Developer PC - Windows 7, VS2012, .NET 4.5 installed during VS2012 install:

unixEpoch 621355968000000000 Utc 
asLocal1 635121441023588986 Local 
asLocal2 635121441023588986 Unspecified

Production Server 1 - Server 2008R2, .NET 4.0

unixEpoch 621355968000000000 Utc 
asLocal1 635121441023588986 Local 
asLocal2 635121441023588986 Unspecified

Production Server 2 - Server 2008R2, .NET 4.5 installed as standalone package

unixEpoch 621355968000000000 Utc
asLocal1 ***635121405023588986*** Local
asLocal2 635121441023588986 Unspecified

Other than having .NET 4.5 installed, production servers 1 and 2 are identical. The problem manifests when run in several different local timezones around the globe.

Sample code that demonstrates the problem:

using System;
using NUnit.Framework;
namespace DateTimeToLocal
{
   [TestFixture]
   public class DateTimeFixture
   {
      private const long unixTimeInNanos = 1376561702358898611;

      [Test]
      public void Demonstrate()
      {
         DateTime unixEpoch = new DateTime(1970, 01, 01, 0, 0, 0, DateTimeKind.Utc);
         DateTime utc = unixEpoch.AddTicks(unixTimeInNanos / 100);

         // Method 1 - doesn't work on 2008R2 with .NET 4.5
         DateTime asLocal1 = utc.ToLocalTime();

         // Method 2 - works across all .NET 4.0 and .NET 4.5
         TimeZoneInfo localTz = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.StandardName);
         DateTime asLocal2 = TimeZoneInfo.ConvertTimeFromUtc(utc, localTz);

         Console.WriteLine("unixEpoch {0} {1}", unixEpoch.Ticks,unixEpoch.Kind);
         Console.WriteLine("asLocal1 {0} {1}", asLocal1.Ticks, asLocal1.Kind);
         Console.WriteLine("asLocal2 {0} {1}", asLocal2.Ticks, asLocal2.Kind);

         Assert.AreEqual(asLocal1, asLocal2);
      }

      public static void Main(string[] args)
      {
         var t = new DateTimeFixture();
         t.Demonstrate();

      }
   }
}
like image 203
Dave Moore Avatar asked Aug 19 '13 15:08

Dave Moore


1 Answers

This problem goes away when the following hotfix is applied to the server running 2008R2: http://support.microsoft.com/kb/2863058/en-us

It appears that under the hood, DateTime.ToLocalTime() uses a lookup technique that fails unless the timezone database update contained in that hotfix has been applied.

This was extremely hard to track down, and I've not seen ANY other web forum mentioning the link between that database update and something as fundamental as utc.ToLocalTime() failing for dates in August 2013, no where near a recent boundary that changed due to legislation, etc. in the Eastern U.S. Still wondering how exactly this is not being seen more places?

like image 63
Dave Moore Avatar answered Oct 01 '22 03:10

Dave Moore