Exploring Windows Time Zones with System.TimeZoneInfo [Josh Free]

The main feature of the System.TimeZoneInfo class (previously named System.TimeZone2 in CTPs prior to .NET Framework 3.5 Beta 1) is to enable .NET developers to seamlessly work with Windows time zones in their applications. This includes enabling .NET applications to take advantage of the new Windows Vista Dynamic Daylight Saving Time functionality, which allows the operating system to store historically accurate time zone information (this includes both past and future time zone data).

With the release of .NET Framework 3.5 Beta 1, the BCL team has received several questions regarding time zone support in Windows and the differences between time zone support on Windows XP and on Windows Vista. In this article, I’ll attempt to explain a little bit about how time zones work in Windows so that .NET developers can have a better understanding of what happens under the hood when they use System.TimeZoneInfo to perform common time zone related tasks in their code.

Time Zones and the Windows Registry

Windows computers store time zone data in the Windows Registry. Before discussing the storage layout of time zones in the registry, I want to issue a standard disclaimer about modifying the Registry (e.g., please don’t email me if you inadvertently destroy your computer while changing registry values with regedit.exe. :-) ):

WARNING: Using Registry Editor incorrectly can cause serious problems that may require you to reinstall your operating system. Microsoft cannot guarantee that problems resulting from the incorrect use of Registry Editor can be solved. Use Registry Editor at your own risk.

Time Zones Registry Hive

All time zones installed on the computer are stored in the following registry hive:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

Each time zone has its own unique key under this registry hive. Sub-keys are used to store information about the time zone such as the display name, standard name, daylight name, and the optional daylight start and daylight end times.

Alaskan Standard Time

TimeZoneInfo Instance Public Properties Example String Returned from Property HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\[Time Zone] Sub-Key Name
TimeZoneInfo.Id Alaskan Standard Time <Time Zone Root Key Name>
TimeZoneInfo.DisplayName (GMT-09:00) Alaska MUI_Display’ on Vista or ‘Display’ on down-level
TimeZoneInfo.StandardName Alaskan Standard Time MUI_Std’ on Vista or ‘Std’ on down-level
TimeZoneInfo.DaylightName Alaskan Daylight Time MUI_Dlt’ on Vista or ‘Dlt’ on down-level
TimeZoneInfo.BaseUtcOffset -09:00:00 TZI’ (REG_TZI_FORMAT struct)
TimeZoneInfo.SupportsDaylightSavingTime True TZI’ (REG_TZI_FORMAT struct)

As you can see above, the display strings are loaded either from the Multilingual User Interface (MUI) DLL, tzres.dll, or straight from the registry, when MUI support is unavailable. MUI-enabled operating systems such as Windows Vista contain MUI_Display, MUI_Std, and MUI_Dlt keys, which are indirectly controlled by the operating systems regional settings. On down-level platforms such as Windows XP and Windows Server 2003, only the Display, Std, and Dlt keys exist. The Display, Std, and Dlt key values are localized only in the default language of the operating system. Because of the Windows time zone registry architecture, CurrentUICulture settings do not impact the values of these TimeZoneInfo properties.

Here is a sample program that demonstrates the use of the TimeZoneInfo properties described above:

using System;

 

public class TimeZoneInfoSample {

    private static void Main() {

        String id = "Alaskan Standard Time";

        TimeZoneInfo tzi;

 

        try {

            tzi = TimeZoneInfo.FindSystemTimeZoneById(id);

        }

        catch (TimeZoneNotFoundException e) {

            Console.WriteLine(id + " not found on the local computer: " + e);

            return;

        }

        catch (InvalidTimeZoneException e) {

            Console.WriteLine(id + " is corrupt on the local computer: " + e);

            return;

        }

 

        Console.WriteLine("TimeZoneInfo.Id = " + tzi.Id);

        Console.WriteLine("TimeZoneInfo.DisplayName = " + tzi.DisplayName);

        Console.WriteLine("TimeZoneInfo.StandardName = " + tzi.StandardName);

        Console.WriteLine("TimeZoneInfo.DaylightName = " + tzi.DaylightName);

        Console.WriteLine("TimeZoneInfo.BaseUtcOffset = " + tzi.BaseUtcOffset);

        Console.WriteLine("TimeZoneInfo.SupportsDaylightSavingTime = " +

            tzi.SupportsDaylightSavingTime);

    }

}

Dynamic Daylight Saving Time (Dynamic DST)

The Dynamic DST sub-key stores historical time zone data.  Windows Vista comes pre-installed with this historical time zone data for many time zones.  Windows XP and Windows Server 2003 users can download the February 2007 cumulative time zone update (KB931836) to get these registry keys: https://support.microsoft.com/kb/931836.  Windows XP and Windows Server 2003 operating systems do not currently use the Dynamic DST data by default (even when the data exists after the KB931836 update is installed), but System.TimeZoneInfo is smart enough to use the Dynamic DST historical data when it exists, on any operating system version.

Alaskan Standard Time Dynamic DST

A Word about Local Time Zone and the ‘Automatically adjust clock for Daylight Saving Time’ Checkbox

TimeZoneInfo contains the static property “TimeZoneInfo.Local” which loads the current, local time zone off of the computer and returns a TimeZoneInfo object.  While loading the local time zone, TimeZoneInfo checks the operating system settings to see whether or not daylight saving time should be obeyed.

Computer users generally control this setting through the Date and Time control panel — specifically the ‘Automatically adjust clock for Daylight Saving Time’ checkbox:

Date and Time Control Panel

Depending on the version of Windows being used, this checkbox will set either the “DisableAutoDaylightTimeSet” or the “DynamicDaylightTimeDisabled” registry key values to one (1):

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation

        "DynamicDaylightTimeDisabled"=dword:00000001

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation

        "DisableAutoDaylightTimeSet"=dword:00000001

Local Time Zone

When daylight saving time is disabled for the local time zone, “TimeZoneInfo.Local” will return a TimeZoneInfo object with “TimeZoneInfo.SupportsDaylightSavingTime”set to False.  Any TimeZoneInfo.ConvertTime(...) calls using this TimeZoneInfo instance will not take daylight saving time into account.

Additional Resources

Comments

  • Anonymous
    June 07, 2007
    I and others have asked many times over the course of more than a year. But there has never been a response. Why must you create more proprietary solutions for something that already exists? Take a look at tz: http://www.twinsun.com/tz/tz-link.htm A thin wrapper class around it would be ideal, rather than this new TimeZoneInfo. Why are we still messing with the registry in this day and age... For all but the most unique requirements, that is an automatic bug in my book. Next point, which is slightly off topic, but still time-related. The present DateTime class is unacceptable. I would like a UTC-only DateTime class. Timezones can be handled as offsets from that. Sure, I can use the existing DateTime class as UTC, but why must I make the exact same settings ever single time? There is always the chance that I will forget somewhere and end up needing to fix a bug later. If you are not willing to do the work, then let me. However, you have sealed the type and not offered any interfaces. Either 1) unseal it (easiest), and / or 2) create an IDateTime interface, apply it to DateTime, and fix all necessary signatures (the better solution in the end, but much more work and less likely.)

  • Anonymous
    June 07, 2007
    Hi Chronos, Thanks for your comments. >> Re: tzinfo The TimeZoneInfo feature is a wrapper around the time zone functionality that is already provided by the Windows operating system today.  Also, TimeZoneInfo does provide the necessary APIs for a developer to import the tzinfo data into custom time zones that are not currently present in Windows. >> Re: UTC-DateTime Stay tuned for future blog posts :-)

  • Anonymous
    June 07, 2007
    Josh, Thanks for the response. I have a few comments. > The TimeZoneInfo feature is a > wrapper around the time zone > functionality that is already > provided by the Windows > operating system today. As this post mentions, though, some of that functionality is only available in Vista. An update (KB931836) will need to be installed for earlier versions. And when future updates are made (as there recently were earlier this year), then more updates will need to be installed. As a developer, I can not assume that all clients will have the necessary updates installed. So I suppose that I will need to always import tc into custom timezones to ensure functionality. Which brings us back to the original situation. Wrapping tc itself would free the need to install updates and would be platform independent. I am really looking forward to any progress or developments regarding a UTC-DateTime type. Please keep us informed.

  • Anonymous
    June 07, 2007
    The comment has been removed

  • Anonymous
    June 07, 2007
    This does not solve the obvious missing piece of the DateTime object...no time zone information.  So..if I use DateTime anywhere and it get's consumed in the another time zone there is NO WAY of knowing exactly when the event happened. Wierd that after all this time (no pun intended) this has not been addressed.

  • Anonymous
    June 07, 2007
    David, We actually have a new type in the .NET Framework 3.5 Beta 1 called DateTimeOffset.  This new date time structure is made up of a date time and an offset relative to the UTC time zone.  DateTimeOffset includes most of the functionality of the current DateTime and allows seamless conversion to DateTime as well.  TimeZoneInfo also works with both DateTime and DateTimeOffset. We'll have more information on DateTimeOffset in a future blog post.

  • Anonymous
    June 07, 2007
    I look forward to proper time zone handling in the future. But I have to write apps today that handle timezones. Any articles/links on converting UTC to/from any arbitrary time zone in .NET today ? This is for browser apps, where converting to user's time would be done on the middle-tier web-server. There would be lots of users from different time zones, so ToLocalTime in the middle tier doesn't work, as there are many local times. (Ok for smart-client, but not browsers). I assume it's all obtainable from the Win32 API, but I'd hate to re-invent the wheel if it's already been done. Thanks.

  • Anonymous
    June 08, 2007
    Hi Andy, Please see this bcl team blog post which contains a download link with the sample time zone code you requested: http://blogs.msdn.com/bclteam/archive/2006/04/03/567119.aspx I hope this helps. Thanks, Josh

  • Anonymous
    June 08, 2007
    Thanks for chosing a better name than  System.TimeZone2. There was a lot of debate over the original name; I'm glad that the BCL team decided to go with a better name. Looking forward to that post on the new DateTime structures :-)

  • Anonymous
    June 08, 2007
    Anders, Thanks for your comments on the class change.  I like the new name much better as well. :-)

  • Anonymous
    June 12, 2007
    The comment has been removed

  • Anonymous
    June 15, 2007
    Josh, thanks for the link to the .NET 2.0 code, excellent!

  • Anonymous
    June 18, 2007
    For the existing .NET 2.0 sample: On a multi-lingual app which may run on any localized version of Windows XP/Windows Server 2003 (US, Japanese, French, etc.), what value do we use to uniquely identify a timezone ? The Registry Key, DisplayName, StandardName, Index, or something else, and will that be the same on Vista ? Does the DisplayName vary depending on the language of the O/S ? If so, I assume we should display the DisplayName, but store the StandardName behind the scenes - is that correct ? Another issue is mapping windows timezones to Java timezones (i.e. Olson names). I'll need to build that myself in .NET 2.0, but could .NET 3.5 provide a GetOlsonName() method ?

  • Anonymous
    June 21, 2007
    The comment has been removed

  • Anonymous
    July 01, 2007
    I wrote the following tz database support (purely in C#) in the PublicDomain package for exactly the points chronos and Andy raise above: http://www.codeplex.com/PublicDomain The TzDateTime class wraps the DateTime class (because I couldn't extend it), as well as exposing the TimeZone, which can be serialized as a string across any wire. The TzTimeZone class then represents all current and historical data extracted from the tz database. Finally, the TzDatabase class reads the tz database into logical form, emits C#, which is then placed into a static initializer in the PublicDomain.dll. This means that the only way to update a tz database change is to send out a new version of PublicDomain.dll. I am open to criticism on this strategy, but I wanted to avoid having to read resources files from the DLL or from the filesystem to lessen the impact of consumers of PublicDomain not to require FileIOPermission. By the way, all of this support is very experimental, and I would be very excited for comments, questions, criticisms, or help to code more of it! Thanks and I am glad that Microsoft continues to work on the current time zone support. Kevin Grigorenko

  • Anonymous
    September 13, 2007
    Visual Studio (VS) 2008 and .Net 3.5, code named “Orcas”, will be released in the near future. This release

  • Anonymous
    October 22, 2007
    Mein TechTalk ist nun zu Ende. Meine letzte Station heute in Berlin war Lustig und Amüsant, ich hoffe

  • Anonymous
    November 19, 2007
    .NET Framework 3.5 and Visual Studio 2008 have officially shipped! Soma has the announcement on his blog

  • Anonymous
    December 05, 2007
    If you are following blogs about .net 3.5 you might have across this interesting post . It explains the

  • Anonymous
    February 06, 2008
    If you are following blogs about .net 3.5 you might have across this interesting post . It explains the

  • Anonymous
    February 06, 2008
    If you are following blogs about .net 3.5 you might have across this interesting post . It explains the