This article was spurred by discussions on the Channel 9 forums around the time of the daylight changes this year. Some users were complaining that the site was not indicating the posting time accurately. I suggested a route forward but failed to convince the administrators. This article is an attempt to provide code that is useful for converting a time stamp into the end-user's own time zone.
Another group of users also need to deal with the time zones: travelers wishing to know the time at their destination, or the time at home - for example, when trying to call relatives or colleagues. The accompanying application, a demonstration of the
TimeZoneInformation class, is useful here. It could also be useful for working out when a Webcast, for example, occurs in your local time zone.
Different countries and locations around the world use different time zones - to match the time shown on a clock with the approximate local time observed. The simplest description is an offset from UTC - Universal Time (Coordinated) - although often erroneously described as an offset from GMT - Greenwich Mean Time. GMT can describe the time zone used in Britain, or a specific offset (UTC+0). The issue is complicated by the Daylight Savings Time; some locations observe daylight savings whereas others do not. Even when they do, they do not agree on the dates and times at which the changes to and from Daylight Savings occur.
Keeping an accurate record of the time zones used around the world is a hard task. Local administrations make rules about the local offset from UTC, and about whether to observe daylight savings, and if so, when. Fortunately, Windows has a database of time zone information installed on every system. Unfortunately, Microsoft failed to provide an API for querying this database.
In addition, Windows provides APIs for discovering the currently selected time zone, for converting from UTC to a specified time zone's local time, and (Windows XP and Server 2003 only) for converting from local time, in a specified zone, to UTC.
.NET's base class library offers the
System.TimeZone class. This class offers information about the current time zone. However, it does not offer any information about other time zones - what their names are, their offsets, or their daylight savings rules. This class is
abstract, so could be extended. I have not yet done that as some of the features (
IsDaylightSavingTime) will be difficult to implement, due to the apparent lack of OS support. A route of investigation would be to inspect the implementation of
System.TimeZone in the Shared Source CLI [^].
The time zone database
Windows NT's time zone database is stored in the registry, at
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones. Under this key are the subkeys describing each time zone. Each subkey has the following values (examples taken from the GMT Standard Time key):
||(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London|
|Name for the zone during daylight savings
||GMT Daylight Time|
|Unique index number for zone
|Unknown. May be related to Win95 clickable time zone map.
|Name for the zone outside daylight savings
||GMT Standard Time|
|Offsets and savings start/end date
The TZI value is the key part. It contains the offset from UTC, the additional offset for daylight savings, and the start and end dates for daylight savings. The structure is defined as follows:
[StructLayout( LayoutKind.Sequential )]
private struct TZI
public int bias;
public int standardBias;
public int daylightBias;
public SYSTEMTIME standardDate;
public SYSTEMTIME daylightDate;
daylightBias fields follow the rules for the corresponding members of the Win32
TIME_ZONE_INFORMATION structure: UTC = local + bias. A negative value indicates that the zone is ahead of UTC (typically east of London) while a positive one indicates behind UTC (typically west of London).
TimeZoneInformation class provides access to the time zone database through the
static method. It exposes an
Index property matching the
Index registry value. The value of this field could be stored in a database, to persist, for example, a website user's time zone selection. I do not expect this value to change between operating system versions.
The operating system provides two APIs for converting times relative to a time zone:
TzSpecificLocalTimeToSystemTime. The former is available only on NT-based operating systems, while the latter is only present on Windows XP and Server 2003 at the time of writing. To use these with a .NET
DateTime structure, we must convert the
DateTime to a
TZI to a
TIME_ZONE_INFORMATION, and convert the resulting
SYSTEMTIME back to a
DateTime conversions to and from
SYSTEMTIME are performed via a
FILETIME using the
TZI to a
TIME_ZONE_INFORMATION is simply a matter of assigning the corresponding fields.
TimeZoneInformation class converts from UTC to time-zone relative local time using the
FromUniversalTime method, and from a time-zone relative local time to UTC using the
ToUniversalTime method. This latter method will only work on Windows XP or Windows Server 2003; on down-level operating systems (Windows NT 4.0, Windows 2000) it throws a
The sample application
The supplied sample application converts either the current time, or a user-selected date and time, from the user's current time zone (or a selected time zone) to a user-selected time zone. The time zone selection defaults to the user's current time zone, so initially, the destination time will be the same as the current zone.
If the user selects Use Current, the local and the destination time update with the system clock.
If the Time Zone checkbox under Local Time is selected, the corresponding drop-down list can be used to select the time zone to convert from. On Windows 2000 or earlier, if this checkbox is selected, an error message will be displayed and the checkbox is subsequently disabled, reflecting the fact that the feature cannot be supported. The selected date and time are converted from the source time zone to UTC, and then from UTC to the destination zone.
I admit that the user interface is basic! I'm no graphic designer. Nevertheless, I hope it will be useful.
- Version 1.2
TimeZoneInformation class: A bug-fix was made to the
static property to cope with the 'Automatically adjust clock for daylight saving changes' checkbox in the Date and Time control panel applet being unchecked. If this checkbox is unchecked, the
GetTimeZoneInformation call returns a structure where
DaylightName are equal to their standard equivalents. This caused the comparison to fail.
- World clock application: A bug-fix to prevent a crash if no time zone was selected in the Source Zone drop-down when the checkbox was checked.
- Version 1.1
TimeZoneInformation class: The
ToUniversalTime method was added to perform conversions to UTC using the
TzSpecificLocalTimeToSystemTime API. Other new methods:
static method to locate a
TimeZoneInformation object for a recorded
Index, and static overloads of
ToUniversalTime which take an
- World clock application: The Local Time Zone checkbox and drop-down list, allowing the user to specify the zone to convert from.