What time is it? Simple enough question, but right now, at this exact point in time, there could be several people reading this question in different areas of the world. The answer for each would be different even though it is the exact same temporal moment. Here on planet Earth, the differences all have to do with time zones and we all pretty much know what those are. What's a little more difficult is how that could impact your code.
If someone submits an order and that time is recorded, what time was the order submitted? To answer that, you have to choose a time zone for the display. Times are specified as Zero or Zulu Time (GMT/UTC) + or - some bias according to how far you are from that zone. If you are east of the UTC zone, the bias increases up to the International Date Line. If you are west of the UTC zone, the bias decreases up to the same line. Here in California, we are in the U.S. Pacific zone, which is UTC – 8 hours. So if the order got recorded using UTC time, then we could display that same value here – 8 hours and it would result in the same exact time for the U.S. Pacific zone. A person on the eastern seaboard (let's say N.Y.) would be in UTC – 5, so there the date display would be UTC – 5 hours.
What .NET Does for You
The .NET DateTime type stores not only the date & time, but the bias as well. It is therefore able to translate back and forth from UTC. Since the bias is part of the value, when you serialize and deserialize the value, .NET is also able to calculate the difference in bias between what's in the date/time and what the local time should be, so it sets the date/time value properly for the destination. In other words, let's say a server in N.Y. serializes the date/time value, which is then transmitted to California and deserialized. The value in California would show the same time minus 3 hours. (You can of course, tell serialization to not adjust for time zone bias).
In .NET 2.0 or 3.0, you are also able to easily convert from local time to UTC and vice-versa because .NET knows all about the local bias.
What .NET Doesn’t Do for You
Most of the time zone functionality in the 2.0 and 3.0 BCL somewhat mirrors what's available in the Win32 API. Unfortunately, this translates into – you only get to deal with Local time and UTC. Usually that's not a problem, but if you are dealing with a server-based application that handles data for multiple time zones, you could be a little screwed with 2.0 or 3.0. There's no automatic functionality to translate dates from an arbitrary time zone (that isn’t the local server time zone) to some other arbitrary time zone or UTC. Also, from what I understand, the DateTime type stores the bias, but not the exact time zone (or time zone identifier) which could be important as we'll see in a minute.
How to Get Around
Fortunately, there are a few alternatives. Some 3rd party libraries do expose this functionality based on local or service databases. Alternately, most versions of Windows store alternate time zone information in the registry, allowing you to do all the calculations manually. The information is stored under the HKEY_Local _Machine\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones key. Each child key represents a different time zone. Most of the values in those time zone keys are self-evident, except the really important one: TZI. TZI is binary data that tells you all about the bias information. You can extract the data by using the BitConverter utility class or unsafe code. The first 4 bytes represent an integer that tells you how many minutes are in the normal bias for the time zone. You might be saying “Minutes? Shouldn't it be hours?” No, there are some time zones that have a 1/2 hour bias, for example. The next value is the Standard Time Bias. This has to do with daylight saving time. When you are NOT in daylight saving time – that is, when you are in standard time – this is an additional bias from the previous normal bias. The value is usually 0 (zero). The next 4 bytes represent an additional bias for daylight saving time (usually 1 hour [60 minutes] if the time zone uses daylight saving time at all). The next bunch of bytes are two SYSTEMTIMEs representing the start date/time of standard time and daylight saving time each year, respectively.
So calculation is pretty straight forward – you can always use the bias and daylight saving time info to convert from a specific time zone to UTC and back, right? Actually, there’s a little more to it than that.
First, there's fallback. This is an edge case that happens on times that fall in the wedges between standard time and daylight saving time during changes. Daylight saving time advances time (usually by an hour) so you end up losing an hour. That day only has 23 hours. On the opposite clock change, you set time back an hour, so you end up with 25 hours in the day, and the same hour could appear twice. If your adjusted time happens to fall into those cracks, you have to take it into account.
Next, you have different daylight saving time periods. There are quite a few time zones and even small regions within a given zone. Some don't use daylight saving time at all. But if they do, daylight saving time only started after a certain year (depending on which zone). If a historical date falls into periods before daylight saving time was established, then it shouldn't be adjusted for DLT. Furthermore, the start and end dates of DLT could change. 2007 in the U.S. is a perfect example. Congress decided to move the change forward a few weeks. So DLT bias calculation prior to October 2007 is different from DLT bias calculation after that date. From a coding perspective, Windows only gives you so much data to work with. You'll notice that some time zone registry keys have a subkey called Dynamic DST. This represents the DLT rule changes, if more than one set of rules apply to that zone, but it is fairly limited in historical context.
Which finally brings us to UTC itself. Coordinated Universal Time (UTC) replaced GMT in 1972. Technically, all date/times prior to 1972 are based on GMT, and all after that period are based on UTC. UTC is based on extremely precise atomic clock adjustments each year, while GMT is not, but for most intents and purposes, the differences are ignored.
Anyway, if you are using .NET 3.5, you are lucky because TimeZoneInfo.FindSystemTimeZoneById gives all this implementation for free (some historical date adjustments notwithstanding). If you're still stuck with 2.0 or 3.0, then you have to take all I covered into account.