Convert time between time zones

I use the TimeZoneInfo.ConvertTime method to convert time from one to another.

When converting Date Time 1/1/2006 2.00 AM from Perth to Sri Jeyawardenepura its conversion on 1/31/2005 11:30 pm

While converting the same time back (1/31/2005 11:30 pm) from Sri Dzheyavardenepura to Perth, it was converted on 1/1/2006 at 3.00 in the morning.

Why is the time zone difference in time mode?

+6
source share
4 answers

Wow, this is Double Whammy! I just stumbled upon this post and was not going to post anything at all, as it is so old and the OP did not show any code. But then curiosity got the best of me, so I tested it.

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

Of course, there is a discrepancy described by the OP. At first I thought that this should be due to some kind of DST problem, so I checked Sri Lanka and Perth . Although both options had a transition in 2006, none of them were close to that date. However, I thought I should check with DateTimeOffset to avoid any ambiguity problems:

 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 he believes that the target time should be at +09:00 , but Perth did not switch to it until December 3, 2006. In January, it was clearly still +08:00 .

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

First let me 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, this seems to be fixed, right? If this is the case, it will mean that the problem is not with the Windows time zone data, as the Noda Time BCL provider uses accurate data. So there should be something really defective in TimeZoneInfo.ConvertTime . There is Whammy # 1 .

So, just to make sure that everything is fine and good, try to do the same with IANA TZDB data. In the end, it became more accurate:

 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, Whammy # 2 . Note that the average time uses an offset of +06:00 ? I thought this was a mistake, but when I checked again here , it turned out that the TZDB data was correct. At that time, Sri Lanka was at +06:00 . He did not switch to +05:30 until April.

So, to indicate Whammys:

  • The Windows TimeZoneInfo.ConvertTime function appears to be erroneous.
  • Incorrect data in the Windows time zone for the "Sri Lanka Standard Time" zone.

Best to just use Noda Time and TZDB!

UPDATE

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

I delved into the source code for the .NET Framework link, and I believe this happens in the TimeZoneInfo.GetIsDaylightSavingsFromUtc private static method. I believe that they do not take into account that the DST does not always start and stop in the same calendar year.

In this case, they apply the 2006 adjustment rule from 2005 and receive endTime from 1/2/2005 to startTime from 12/4/2005 . They are trying to reconcile that this should be in 2006 (by incorrectly adding a year), but they do not believe that the data is in the reverse order.

This problem is likely to be displayed for any time zones that start their DST in the winter (for example, in Australia), and it will be displayed in one form or another at any time when the transition rule changes - which was done in 2006.

I added the problem to Microsoft Connect here .

The “second drunk” that I mentioned is just because historical data for Sri Lanka does not exist in the Windows time zone registry keys.

+11
source

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

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 very strange and may be due to the Libyan time zone gap - although it does not have two transitions, only one is unulocal.

+6
source

You will need to publish specific code to be sure. A problem may arise, for example. however, daylight saving time is applied by one conversion, but not by another.

Possible nuances of time zone management. Have a look at this Jon Skeet blog for a great review.

In fact, it is so difficult to use .NET time classes correctly that John executed the Joda-Time port in .NET, Noda Time .

This is a serious consideration to consider for any project that supports multiple time zones.

+3
source

Have you considered the daily time savings during conversion? See the following link and you will get a response. The displayed time is absolutely correct.

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

+1
source

Source: https://habr.com/ru/post/912365/


All Articles