9

I'm using TimeZoneInfo.ConvertTime method to convert time from one to another.

While converting the Date Time 1/1/2006 2.00 AM from Perth to Sri Jeyawardenepura its converted to 1/31/2005 11.30pm

While converting the same time back (1/31/2005 11.30pm) from Sri Jeyawardenepura to Perth its converted to 1/1/2006 3.00 AM.

Why there is one hour difference in the Time Zone conversion?

LokiDil
  • 197
  • 1
  • 3
  • 13
  • Do you need this manual conversion or may you use the DateTimeOffset class having all this conversion handling for your ? – Boas Enkler Apr 04 '12 at 08:35
  • 1
    Can post a code sample that exhibits this issue? – CodesInChaos Apr 04 '12 at 09:00
  • @BoasEnkler Thanks,Its not a manual conversion,Will try DateTimeOffset – LokiDil Apr 04 '12 at 10:00
  • @CodeInChaos here is the sample code:- timeBlock.StartTimeStamp = TimeZoneInfo.ConvertTime(timeBlock.StartTimeStamp, fromTimeZone, toTimeZone); timeBlock.EndTimeStamp = TimeZoneInfo.ConvertTime(timeBlock.EndTimeStamp, fromTimeZone, toTimeZone); – LokiDil Apr 04 '12 at 10:01
  • 1
    @LokiDil that's not enough. Please create a working example, in particular the code which gets `from/toTimeZone`. I suspect the offset of one of those timezones changed at that specific point in time. – CodesInChaos Apr 04 '12 at 10:21

4 Answers4

12

Wow, this is a Double Whammy! I just stumbled across this post and wasn't going to post anything at all since it's so old and the OP didn't show any code. But then curiosity got the best of me so I checked it out.

Using just the .NET BCL:

string tzid1 = "W. Australia Standard Time"; // Perth
TimeZoneInfo tz1 = TimeZoneInfo.FindSystemTimeZoneById(tzid1);

string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
TimeZoneInfo tz2 = TimeZoneInfo.FindSystemTimeZoneById(tzid2);

DateTime dt1 = new DateTime(2006, 1, 1, 2, 0, 0);
Debug.WriteLine(dt1); // 1/1/2006 2:00:00 AM
DateTime dt2 = TimeZoneInfo.ConvertTime(dt1, tz1, tz2);
Debug.WriteLine(dt2); // 12/31/2005 11:30:00 PM
DateTime dt3 = TimeZoneInfo.ConvertTime(dt2, tz2, tz1);
Debug.WriteLine(dt3); // 1/1/2006 3:00:00 AM

Sure enough, there is the discrepancy that the OP described. At first I thought this must be due to some kind of DST issue, so I checked for Sri Lanka and Perth. While both had a transition in 2006, neither were anywhere close to it for this date. Still, I thought I should check using DateTimeOffset to avoid any ambiguity issues:

string tzid1 = "W. Australia Standard Time"; // Perth
TimeZoneInfo tz1 = TimeZoneInfo.FindSystemTimeZoneById(tzid1);

string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
TimeZoneInfo tz2 = TimeZoneInfo.FindSystemTimeZoneById(tzid2);

DateTime dt = new DateTime(2006, 1, 1, 2, 0, 0);
DateTimeOffset dto1 = new DateTimeOffset(dt, tz1.GetUtcOffset(dt));
Debug.WriteLine(dto1);  // 1/1/2006 2:00:00 AM +08:00
DateTimeOffset dto2 = TimeZoneInfo.ConvertTime(dto1, tz2);
Debug.WriteLine(dto2);  // 12/31/2005 11:30:00 PM +05:30
DateTimeOffset dto3 = TimeZoneInfo.ConvertTime(dto2, tz1);
Debug.WriteLine(dto3);  // 1/1/2006 3:00:00 AM +09:00

And it's still off. You can see that it thinks the target time should be at +09:00, but Perth didn't switch to that until December 3rd 2006. In January it was clearly still +08:00.

So then I thought... Noda Time to the rescue!

First let's check using the same Windows .NET BCL time zones.

string tzid1 = "W. Australia Standard Time"; // Perth
DateTimeZone tz1 = DateTimeZoneProviders.Bcl[tzid1];

string tzid2 = "Sri Lanka Standard Time"; // Sri Jeyawardenepura
DateTimeZone tz2 = DateTimeZoneProviders.Bcl[tzid2];

LocalDateTime ldt1 = new LocalDateTime(2006, 1, 1, 2, 0, 0);
ZonedDateTime zdt1 = ldt1.InZoneStrictly(tz1);
Debug.WriteLine(zdt1.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
ZonedDateTime zdt2 = zdt1.WithZone(tz2);
Debug.WriteLine(zdt2.ToDateTimeOffset()); // 12/31/2005 11:30:00 PM +05:30
ZonedDateTime zdt3 = zdt1.WithZone(tz1);
Debug.WriteLine(zdt3.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00

Hey, that seems like it fixed it, right? If so, that would mean that the problem isn't with the Windows time zone data, because Noda Time's BCL provider uses the exact same data. So there must be something actually defective in TimeZoneInfo.ConvertTime. There's Whammy #1.

So just to check that it's all good and well, let's try the same thing with IANA TZDB data. It's known to be more accurate after all:

string tzid1 = "Australia/Perth";
DateTimeZone tz1 = DateTimeZoneProviders.Tzdb[tzid1];

string tzid2 = "Asia/Colombo"; // Sri Jeyawardenepura
DateTimeZone tz2 = DateTimeZoneProviders.Tzdb[tzid2];

LocalDateTime ldt1 = new LocalDateTime(2006, 1, 1, 2, 0, 0);
ZonedDateTime zdt1 = ldt1.InZoneStrictly(tz1);
Debug.WriteLine(zdt1.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00
ZonedDateTime zdt2 = zdt1.WithZone(tz2);
Debug.WriteLine(zdt2.ToDateTimeOffset()); // 1/1/2006 12:00:00 AM +06:00
ZonedDateTime zdt3 = zdt1.WithZone(tz1);
Debug.WriteLine(zdt3.ToDateTimeOffset()); // 1/1/2006 2:00:00 AM +08:00

And there, my friends, is Whammy #2. Notice that the middle time is using a +06:00 offset? I thought this was in error, but when I checked once more here it turns out that the TZDB data is correct. Sri Lanka was at +06:00 at that time. It didn't switch to +05:30 until April.

So to recap the Whammys:

  • The Windows TimeZoneInfo.ConvertTime function appears to be flawed.
  • The Windows Time Zone data for the "Sri Lanka Standard Time" zone is incorrect.

All the better to just use Noda Time and TZDB always!

UPDATE

Thanks to Jon Skeet for helping identify that the first problem is with the way that the "W. Australia Standard Time" zone is being interpreted by the TimeZoneInfo class.

I dug much deeper into the .NET Framework reference source code, and I believe this is happening in the private static method TimeZoneInfo.GetIsDaylightSavingsFromUtc. I believe that they are not taking into account that DST doesn't always start and stop in the same calendar year.

In this case, they are applying the 2006 adjustment rule with the 2005 year, and getting an endTime of 1/2/2005 before the startTime of 12/4/2005. They do attempt to reconcile that this should be in 2006 (by incorrectly adding a year), but they don't consider that data is in reversed order.

This problem will probably show up for any time zones that start their DST in the winter (such as Australia), and it will show up in one form or another any time the transition rule changes - which it did in 2006.

I've raised an issue on Microsoft Connect here.

The "second whammy" I mentioned is just because the historical data for Sri Lanka doesn't exist in the Windows time zone registry keys.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Nice analysis. Thanks for the details. – AYK Sep 09 '13 at 09:01
  • The `ConvertTime` part appears to be broken on Perth rather than Sri Lanka. Converting between Perth and UTC has the same problem. EDIT: Yes, there's something very odd going on there. Noda Time doesn't find the transition, but the BCL does in some cases... – Jon Skeet Sep 10 '13 at 19:24
  • @JonSkeet - Interesting. Perhaps something amiss in the Dynamic DST entries for the BCL "W. Australia Standard Time"? – Matt Johnson-Pint Sep 10 '13 at 19:44
  • @MattJohnson: I think the way `TimeZoneInfo` interprets transitions at the start of the year is just broken, basically :( – Jon Skeet Sep 10 '13 at 19:45
  • @JonSkeet - I think it's also fair to say that Sri Lanka data is incorrect since it has no Dynamic DST entries and assumes it was always at +5:30. But I suppose that is true for many other BCL zones as well. – Matt Johnson-Pint Sep 10 '13 at 19:47
  • @MattJohnson: Yes - that's just the Windows time zone data being broken, which happens a lot. – Jon Skeet Sep 10 '13 at 19:47
6

Just to add a little more information to Matt's answer, it seems that the BCL is very confused about its own data for Perth. It seems to think there were two transitions around the end of 2005 - one at 4pm UTC, and one eight hours later.

Demo:

using System;

class Test
{    
    static void Main()        
    {
        var id = "W. Australia Standard Time"; // Perth
        var zone = TimeZoneInfo.FindSystemTimeZoneById(id);
        var utc1 = new DateTime(2005, 12, 31, 15, 59, 0, DateTimeKind.Utc);
        var utc2 = new DateTime(2005, 12, 31, 16, 00, 0, DateTimeKind.Utc);
        var utc3 = new DateTime(2005, 12, 31, 23, 59, 0, DateTimeKind.Utc);
        var utc4 = new DateTime(2006, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        Console.WriteLine(zone.GetUtcOffset(utc1));
        Console.WriteLine(zone.GetUtcOffset(utc2));
        Console.WriteLine(zone.GetUtcOffset(utc3));
        Console.WriteLine(zone.GetUtcOffset(utc4));
    }
} 

Results:

08:00:00 // 3:59pm UTC
09:00:00 // 4:00pm UTC
09:00:00 // 11:59pm UTC
08:00:00 // 12:00am UTC the next day

This is highly bizarre, and may be related to the Libyan time zone breakage - although that doesn't have two transitions, just one misplaced one.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I just manually checked all of the bits from the registry Dynamic DST data for that zone in 2005 & 2006 and it appears to be correct in the data. You must be right that it is in the interpretation. – Matt Johnson-Pint Sep 10 '13 at 20:26
  • I think I found the problem. See the update at the bottom of my answer. – Matt Johnson-Pint Sep 10 '13 at 22:39
  • @MattJohnson: Right. I don't know whether that *is* the same as "the Libya issue" but I wouldn't be surprised. It certainly seems that the code for interpreting adjustment rules is badly flawed. – Jon Skeet Sep 11 '13 at 05:43
3

You would have to post the specific code to be certain. There could be an issue e.g. with daylight time being applied by one conversion but not the other.

There are may nuances of timezone management. Suggest you review this Jon Skeet blog for a great overview.

It is in fact so tricky to correctly use the .NET time classes that Jon has undertaken a port of Joda-Time to .NET, called Noda Time.

It's worth seriously considering for any project that supports multiple time zones.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • I don't have VS2010 to use nupkg :( – LokiDil Apr 04 '12 at 10:18
  • @LokiDil: Apparently you can use Sharp Develop (free) to install NuGet packages in older style VS projects. Have not done that myself though: http://stackoverflow.com/questions/8839009/can-i-use-nuget-with-a-project-built-in-an-earlier-version-of-visual-studio-with – Eric J. Apr 04 '12 at 16:40
  • @EricJ. If you are interested in how I used Noda Time to investigate this issue, please see my answer for details. – Matt Johnson-Pint Sep 08 '13 at 20:44
1

Have you considered Day light savings when converting times ? Refer following link and you will get your answer. The time displayed is absolutely correct

http://www.timeanddate.com/worldclock/timezone.html?n=196&syear=2000

AYK
  • 3,312
  • 1
  • 17
  • 30