Imagine you are developing a Web or desktop application where you need to work with
DateTime values in a time zone specified by the user's preference. I got myself into such a situation several weeks ago and I started to search the Web for a suitable solution. Unfortunately I didn't find anything I liked, so I started to design my own solution.
The first article I took into consideration was Mike Dimmick's WorldClock article here on The Code Project. If you need to convert
DateTime values from/to various time zones, this could be an acceptable solution. But it was not my case exactly because I just was to convert times from/to UTC values, so I came to the conclusion that I needed to extend the abstract
System.TimeZone class provided by the .NET Framework. Furthermore, Mike's code doesn't really comply with my sense of object oriented design, no offence.
You may also find it useful to use the Olson time zone database, but remember that you will need the PublicDomain DLL installed in the GAC and you will have to care about the database updates.
UITimeZone and TimeZoneManager
As you can see in the picture above, the required functionality is implemented by two cooperating classes. There is a
UITimeZone class that derives from the abstract
System.TimeZone class. The
TimeZone class contains a property called
CurrentTimeZone which can be used to obtain the current time zone of the machine executing the calling assembly - in case of a Web application, this could be the time zone of the Web server. Similarly, the
UITimeZone class provides the
CurrentUITimeZone property that can be used to get or set the time zone of the user interface. It is an analogy of the
CurrentUICulture properties of the
TimeZoneManager class has no special meaning; it's just a
static class providing a list of available time zones and an implementation of the Greenwich Mean Time time zone that could be used as a default value in some scenarios. The list of available time zones is retrieved from the time zone database placed in the Windows registry, so you can rely on Microsoft to update the database when needed. But if you want to be perfect, you should monitor the Windows registry for changes and reload the collection of available time zones every time a modification is made to the time zone database.
If you need more detailed information about time zones and about the way time zones are organized in the Windows registry, see the Mike Dimmick's article or refer to the MSDN Library.
There are several reasons why one should not try to use the
System.TimeZone class as a base class. In my Web search for time zone solutions, I've found a thread in the MSDN Forums where a member of the Base Class Libraries development team classifies the attempt to make
TimeZone a point of extensibility as a mistake. First of all, it is unclear what the
ToLocalTime() method should do, whether it should convert from the represented time zone to the machine time zone or from UTC to the represented time zone. I see the latter perfectly suitable for the case of
UITimeZone, because it seems clear to me that
TimeZone.CurrentTimeZone.ToLocalTime() converts to machine local time, and
UITimeZone.CurrentUITimeZone.ToLocalTime() converts to user local time. But I can understand that this could be a matter of opinion.
Another issue does not refer directly to the
TimeZone class, but rather to the internal
CurrentSystemTimeZone class that is used in the implementation of the
TimeZone.CurrentTimeZone property. The problem doesn't appear until you need to work with historical data. Some of the existing time zones use so called Dynamic Daylight Saving Time which means that the dates and times at which the changes to and from Daylight Saving Time occur differ from one year to another. The
CurrentSystemTimeZone class always uses the actual daylight saving time rules, whether the year of the processed time equals to the current year or not. Dynamic daylight changes are stored in the Windows registry, but neither .NET API nor Win32 API (except Windows Vista) provides functionality of querying this data. If you need to work with historical data, which should be the case in database applications and information systems, you may use the
UITimeZone class because it handles historical data correctly.
Using the Code
First of all, you need to implement your own logic of retrieving and updating the user's time zone setting. The sample provided with this article stores the user preferences just in memory, so it is lost when the application ends.
public static TimeZone CurrentUITimeZone
string userName = Thread.CurrentPrincipal.Identity.Name;
string userName = Thread.CurrentPrincipal.Identity.Name;
The following code snippet shows how to use the current user interface time zone. Note that
DateTime values retrieved from a database used to have their
Kind property set to
DateTimeKind.Unspecified, but it may differ with the particular database provider you might be using.
TimeZone currentUITimeZone = UITimeZone.CurrentUITimeZone;
DateTime dbValue = GetDateFromDatabase();
DateTime local = currentUITimeZone.ToLocalTime(dbValue);
local = GetDateFromUser();
dbValue = currentUITimeZone.ToUniversalTime(local);
When you need to modify the user interface time zone, you can use the machine time zone, the Greenwich Mean Time time zone, or you can face the user with a drop-down displaying the list of available time zones which is provided by the
UITimeZone.CurrentUITimeZone = TimeZone.CurrentTimeZone;
UITimeZone.CurrentUITimeZone = TimeZoneManager.GmtTimeZone;
UITimeZone.CurrentUITimeZone = TimeZoneManager.TimeZones;
TimeZoneManager.TimeZones["Central Europe Standard Time"];
Hopefully, enumerating the time zones and more will be soon directly supported by the .NET Framework and its
TimeZoneInfo class. Until the .NET Framework 3.5 is released, you might find my classes useful, and I will appreciate any feedback on them. Also feel free to report any bugs discovered in my code. Thanks.
- 29th July, 2007: Initial post