Introduction
This article describes a simple .NET API for using the ZoneInfo
database, which is also known as the tz
database or Olson
database.
The API is available
here.
I hope you find it useful. :)
Background
I have been developing a web site which schedules meetings and events across multiple time zones for more than a year now.
I previously used the .NET PublicDomain library discussed in another CodeProject article but had a number of issues including:
- Just not working for Australia
- It seems to hard code in the
tz
database rather than reading it from the file system; therefore if you need to update the database, you need to wait for the source code to be updated - The library seems to have a lot of other things in it which I just don't need (which is not a major issue though)
One of the big things I wanted was to have an API by which I can just download the latest tz
database and off I go.
I am not a big fan of reinventing the wheel so if this library just isn't needed, PLEASE let me know. :)
Using the Code
Talking a fairly agile approach to this, I have just implemented the APIs which I have needed.
The interface itself is extremely simple and hopefully is well illustrated by the examples below.
Loading the Data
Database.LoadFiles(@"c:\src\trunk\TimeZoneTest\tzdata");
Before you start to use any of the functions, you need to make sure that the database of files are loaded.
Right now *.tab files in the timeinfo
distribution are not loaded, but they are needed for the current set of APIs.
Getting the Local Time Somewhere Else
foreach (string zoneName in Database.GetZoneNames())
{
Zone z = Database.GetZone(zoneName);
Console.WriteLine("Zone: " + z.Name
+ "\n\thas a local date time of "
+ z.Now.ToString()
+ "\n\twhich is a GMT offset of "
+ z.GetUtcOffset(z.Now).ToString());
}
The sample above gets all the time zone names and then gets the corresponding ZoneInfo.Zone
instance.
From this instance, you can do a number of things like getting the local time in that zone and its current UTC offset.
Zone: Europe/Malta
has a local date time of 8/04/2008 9:40:52 AM
which is a GMT offset of 02:00:00
Zone: Europe/Minsk
has a local date time of 8/04/2008 10:40:52 AM
which is a GMT offset of 03:00:00
Zone: Europe/Monaco
has a local date time of 8/04/2008 9:40:52 AM
which is a GMT offset of 02:00:00
Zone: Europe/Moscow
has a local date time of 8/04/2008 11:40:52 AM
which is a GMT offset of 04:00:00
Convert Times between Time Zones
Zone melbTz = Database.GetZone("Australia/Melbourne");
Zone spTz = Database.GetZone("America/Sao_Paulo");
DateTime melbTime = new DateTime(2009, 1, 1, 0, 0, 0, DateTimeKind.Local);
DateTime utcTime = melbTz.ConvertToUtc(melbTime);
DateTime spTime = spTz.ConvertToLocal(utcTime);
Console.WriteLine("Melbourne " + melbTime.ToString()
+ "\nSao Paulo " + spTime.ToString()
+ "\nUTC " + utcTime.ToString());
One of the most useful features of the API is the ability to convert arbitrary dates between time zones. A lot of TimeZone
APIs only really know what the UTC offset is right now, not 6 months from now.
Melbourne 1/01/2009 12:00:00 AM
Sao Paulo 31/12/2008 11:00:00 AM
UTC 31/12/2008 1:00:00 PM
Offset Cut-over Windows
List<DateTime> dates =
melbTz.GetCutoverWindows(
new DateTime(2007, 1, 1, 0, 0, 0, DateTimeKind.Local),
new DateTime(2009, 12, 31, 23, 59, 59, DateTimeKind.Local));
Console.WriteLine("Printing cutovers between 1/1/2007 - 31/12/2009");
foreach (DateTime dt in dates)
{
Console.WriteLine("\t" + dt.ToString());
}
This functionality may seem useless ... until you actually need it.
If you store dates in the database in UTC and need to convert them to the local time of the user, you need to apply a UTC offset. What happens if the daylight offset changes within this window? Well, you can know if this is the case rather than assuming that it doesn't matter.
Printing cutovers between 1/1/2007 - 31/12/2009
30/03/2007 2:00:00 AM
27/10/2007 2:00:00 AM
6/04/2008 2:00:00 AM
5/10/2008 2:00:00 AM
5/04/2009 2:00:00 AM
4/10/2009 2:00:00 AM
Points of Interest
The code seems to work well for Australia and the other zones I have tested this with. I am sure as more people use it in different scenarios, it will continue to mature.
One thing I did notice was just how un-standard the format of the tz
database was with respect to tab and space delimiting of fields ... the code has a number of hacks unfortunately to compensate this.
More Information
Check out the latest version of the code on CodePlex.
If you have any issues, please log it there and I will look at any changes which need to get done.
History
- 7th April, 2008: Initial post
There are still a number of things to be done including handling of Link entries in the files. The current format allows for backward compatibility in the old names. This is not hard to do and will happen soon (or if someone needs it).