سؤال

I need to convert a 'DateTime' value from different timezones to UTC and vice-versa. I use TimeZoneInfo to do this. But, the issue is when the 'Day Light Saving' time change happens.

For example, this year, next time the time change happens at 2AM[CDT] on November 3. So on Nov 3, 1AM[CDT] is converted to 6AM and as time change happens the next hour we get 1AM[now its CST] again and it is also converted to 6AM. I tried the code on this page, but it didn't say anything how to handle this issue. So how to deal with this issue???

Edit:

I tried NodaTime and when I do the conversion like

 DateTimeZoneProviders.Tzdb["America/Chicago"].AtStrictly(<localDateTime>) 

it throws AmbiguousTimeException. Thats good and I can do this using TimeZoneInfo too. But how do I know which localTime value I need to pick?

Edit 2:

here is the link for the chat discussion with Matt.

هل كانت مفيدة؟

المحلول

If all you have is a local time, and that time is ambiguous, then you cannot convert it to an exact UTC instant. That is why we say "ambiguous".

For example, in the US Central time zone, which has the IANA zone name America/Chicago and the Windows zone id Central Standard Time - covering both "Central Standard Time" and "Central Daylight Time". If all I know is that it is November 3rd, 2013 at 1:00 AM, then then this time is ambiguous, and there is absolutely no way to know whether this was the first instance of 1:00 AM that was in Central Daylight Time (UTC-5), or Central Standard Time (UTC-6).

Different platforms do different things when asked to convert an ambiguous time to UTC. Some go with the first instance, which is usually the Daylight time. Some go with the Standard time, which is usually the second instance. Some throw an exception, and some (like NodaTime) give you a choice of what you want to happen.

Let's start with TimeZoneInfo first.

// Despite the name, this zone covers both CST and CDT.
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var dt = new DateTime(2013, 11, 3, 1, 0, 0);
var utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz);
Debug.WriteLine(utc); // 11/3/2013 7:00:00 AM

As you can see, .net chose to use the "standard" time, which is UTC-6. (Adding 6 hours to 1AM gets you to 7AM). It didn't give you any warning that the time was ambiguous. You could have checked yourself, like this:

if (tz.IsAmbiguousTime(dt))
{
    throw new Exception("Ambiguous Time!");
}

But there isn't anything to enforce this. You must check it yourself.

The only way to not have ambiguity is to not use the DateTime type. Instead, you can use DateTimeOffset. Observe:

// Central Standard Time
var dto = new DateTimeOffset(2013, 11, 3, 1, 0, 0, TimeSpan.FromHours(-6));
var utc = dto.ToUniversalTime();
Debug.WriteLine(utc); // 11/3/2013 7:00:00 AM +00:00

// Central Daylight Time
var dto = new DateTimeOffset(2013, 11, 3, 1, 0, 0, TimeSpan.FromHours(-5));
var utc = dto.ToUniversalTime();
Debug.WriteLine(utc); // 11/3/2013 6:00:00 AM +00:00

Now, compare this to NodaTime:

var tz = DateTimeZoneProviders.Tzdb["America/Chicago"];
var ldt = new LocalDateTime(2013, 11, 3, 1, 0, 0);

// will throw an exception, only because the value is ambiguous.
var zdt = tz.AtStrictly(ldt);

// will pick the standard time, like TimeZoneInfo did
var zdt = tz.AtLeniently(ldt);

// manually specify the offset for CST
var zdt = new ZonedDateTime(ldt, tz, Offset.FromHours(-6));

// manually specify the offset for CDT
var zdt = new ZonedDateTime(ldt, tz, Offset.FromHours(-5));


// with any of the above, once you have a ZonedDateTime
// you can get an instant which represents UTC
var instant = zdt.ToInstant();

As you can see, there are lots of options. All are valid, it just depends on what you want to do.

If you want to completely avoid ambiguity, then always keep a DateTimeOffset, or when using NodaTime use a ZonedDateTime or OffsetDateTime. If you use DateTime or LocalDateTime, there is no avoiding ambiguity.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top