Introduction
Dates, time zones, and overall internationalization are not a programmer's friend, historically. From Julius Caesar in 45 B.C.E. (Julian calendar) through Pope Gregory the XIII in 1582 C.E. (Gregorian calendar), UTC and world-wide calendars, dates and times have shifted to ever-changing politics and new scientific understandings. Although the invention of computing and information exchange is relatively new, many concepts of date and time zone information are not ingrained in the architectures and frameworks we use every day.
Windows time zone support is sufficient, but has drawbacks and is not exposed easily in .NET (requiring registry queries and WIN32 calls).
Enter the Olson Time Zone database, a public domain database of historical time zone data points. It is well maintained and used by Unix, Linux, and Java, just to name a few. It is not used by Windows nor .NET, but can be used with the PublicDomain
package.
PublicDomain
The PublicDomain package is a completely free, public domain collection of oft-needed code and packages for .NET. There is no license, so the code (or any part of it) may be included even in corporate applications. Public domain code has no authority and is provided 'AS-IS'. See this link for more details.
First, An Example
Getting right to the code, here is an example of using the Olson tz database through the PublicDomain DLL (installed in the GAC):
TzTimeZone zone = TzTimeZone.CurrentTimeZone;
zone = TzTimeZone.ZoneUsEastern;
zone = TzTimeZone.GetTimeZone(TzConstants.TimezoneEuropeMoscow);
TzDateTime moscowLocal = zone.Now;
moscowLocal += new TimeSpan(1, 0, 0);
Console.WriteLine(moscowLocal.DateTimeLocal);
Console.WriteLine(moscowLocal.DateTimeUtc);
zone = TzTimeZone.ZoneUsEastern;
Console.WriteLine(zone.GetAbbreviation());
TzDateTime localNow = new TzDateTime(zone.ToLocalTime(DateTime.UtcNow), zone);
Console.WriteLine(localNow.DateTimeLocal.ToString("G"));
Aspects of the code:
TzTimeZone
extends the System.TimeZone
class, providing static
methods for retrieving time zone instances. Names follow the well-known Olson time zone names, such as "America/New_York", "US/Eastern", "Europe/Paris", etc. - The
TzDateTime
class primarily wraps the System.DateTime
class, also storing the time zone for the date/time, and providing both a local version of the DateTime
, and a UTC version. The TzDateTime
class prints and serializes the UTC version by default to systematically push programmers towards using UTC dates/times.
TzDateTime/TzTimeZone Design
- Supported by all .NET 2.0 languages. The setup installs PublicDomain.dll into the GAC, and is located, by default, at C:\Program Files\Public Domain\PublicDomain.dll. All source code for the package is also located in this directory.
- One of the primary issues with time zones is keeping them up-to-date. Java, *nix, and others deal with these issues using patches. If a new version of the Olson time zone database is created, a new version of
PublicDomain
is created and must be installed or re-packaged with client programs. - The entire Olson time zone database is compiled into the PublicDomain DLL package. I chose not to use resource files so as not to require
FileIOPermission
for clients of the package.
- This increases static initialization time dramatically, and that is the trade off.
- The
TzDatabase
class exists primarily at compile time for PublicDomain
. When there is a new version of the Olson time zone database, the data files are updated and a script is run which reads the database, converts it to C# code, which is then placed into TzTimeZone.cs and PublicDomain
is re-compiled. However, the TzDatabase
can be used to get all of the database contents in objectified form, since the database is in an obfuscated form.
Converting Existing Code
The TzDateTime
and TzTimeZone
classes have been designed to make converting almost as simple as doing a large Find & Replace for DateTime
-> TzDateTime
, and TimeZone
-> TzTimeZone
. However, migration is not always as trivial. The TzDateTime
constructors mimic the DateTime
constructors, but also require an added parameter which specifies the time zone, or a boolean
which specifies that the DateTime
provided is a UTC date/time. Time zone support should be architected well, but in general, the recommendation is to create a statically initialized TzTimeZone
in your code which represents the canonical time zone of your application, and use this static
variable where time zones are required. This makes future modification/internationalization easier.
History
- 21st July, 2007: Initial post