Beware the Introduction of DateTime.Kind in .NET 2.0

I just spent the majority of the day fixing some code that was migrated from .NET 1.1 to 2.0. The code in question does a lot of DateTime conversion with respect to timezones. The existing code would basically take the UTC date it was given and adjust it by some arbitrary number of hours representing the timezone offset for a particular facility. It also must take into account that lovely concept known as daylight savings time.

We have a class called ProductionFacility and one of the things we need to know about it is what its timezone offset is from UTC on a particular date to be able to do things like production scheduling and shipping calculations for that facility. Our logic basically looked like this:

public DateTime GetHoursOffsetFromUtc(DateTime utcDateTime)
{
// note: standardTimezoneOffset would be -5 for a facility
// on the east code
  int facilityTimezoneOffset = this.standardTimezoneOffset;
  DateTime facilityLocalStandardTime = utcDateTime.AddHours(facilityTimeZoneOffset);
  DaylightTime facilityDaylightTime = this.GetDaylightTime();
  if(TimeZone.IsDaylightSavingsTime(facilityLocalStandardTime, facilityDaylightTime))
{
facilityTimezoneOffset = this.daylightsSavingsTimezoneOffset;
  }
  return facilityTimezoneOffset;
}

The part highlighted in red is the initial problem spot in .NET 2.0 with the part highlight in orange being the piece that feels a side-effect. You see, with DateTime::Kind you can now actually tell what someone’s intention was with respect to the time aspects of the instance. This is actually great! However, not so great from a conversion aspect because it can easily break existing code. In my example above the DateTime passed into the function originally comes from DateTime::UtcNow which automatically (and correctly) sets its Kind to DateTimeKind.Utc. Once you’ve got a date with a specific Kind, using any of the AddXXX methods results in a DateTime of the same kind. Again, this is a good thing, but something you need to watch out for when converting. The ultimate problem with the above code now though is, guess what happens when you pass a UTC DateTime to TimeZone::IsDaylightSavingsTime? Answer: You get an instant result of false.

So, to work around this, we have to convert the time to a DateTimeKind.Local. Here’s the easiest way I’ve found to do it:

new DateTime(someUtcTime.Ticks, DateTimeKind.Local);

This will give you the same exact date and time, but now it’s considered local to a specific timezone. Now if we replace the red line in the above function with the following logic instead it will fix the problem:

DateTime facilityLocalStandardTime = 
new DateTime(utcDateTime, DateTimeKind.Local).AddHours(facilityTimeZoneOffset);

Hopefully this will save someone else some time. 😉

Note: In case anyone comments without reading everything, keep in mind, I can’t just use DateTime::ToLocalTime here because I’m not trying to convert to the machine’s local time, I’m trying to convert to a facility’s local timezone which isn’t necessarily in the same timezone as the server running the code.

Update 6/1 9:50PM:
I had a feeling there had to be a better way to change a DateTime’s Kind other than constructing a new instance, so I took a look at the SDK and sure enough there is in the form of a static SpecifyKind method on the DateTime structure. Really weird that it’s not an instance method like all the other mutating methods, but uhh… ok. So, the red logic above can now be more easily replaced with the following:

DateTime facilityLocalStandardTime = 
DateTime.SpecifyKind(utcDateTime, DateTimeKind.Local)
.AddHours(facilityTimeZoneOffset);

3 thoughts on “Beware the Introduction of DateTime.Kind in .NET 2.0

  1. Interesting topic Drew, it’s certainly a tip I’ll keep in mind. On my last project we had to deal with timezones as well and it wasn’t very pretty.Sometimes I think I would’ve loved for .NET to just support attaching real zone information to a datetime and having it *not* try to be smart and fiddle with it (the whole automatic conversion to local timezone and the other stuff you mention).

  2. Tomas,Yeah I think everyone wishes .NET’s DateTime supported timezone’s a little better, but given the complexity the subject presents I can totally understand why they didn’t make a stab at it. :)At least with the Kind it makes it a *lot* easier to tell whether a time was intended to be timezone specific or UTC, so it’s a step in the right direction. In the same set of APIs we would repeatedly have callers passing in local time when it needed to be UTC, but in 1.1 all we could do is document that it needed to be UTC. While I was fixing the code I also added in a parameter test to make sure the time was of Kind UTC and throw an exception if it’s not. That will definitely solve a lot of problems going forward! :DCheers,Drew

  3. We recently ran into something like this for WPF/xaml — the standard TypeConverter for DateTime is blissfully unaware of Kind, so if we used that in xaml, our serialization format would be ambiguous. So we’re currently in the process of writing our own replacement (DateTimeSerializer) that implicitly includes the Kind.

Leave a Reply