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

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

  • Comments 21

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: http://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

  • 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.)

  • 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 :-)

  • 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.

  • Yes, it is definitely true that you can never depend on every user's system having the latest patches installed from Windows Update.  It doesn't make too much sense for an application to redistribute static tz data, which will become outdated as countries change their tz rules after the application has shipped.  For standalone client applications the guidance is to rely on the tz data on the local computer, since using this data allows the application to behave consistently with all other applications on the machine.

    For client-server/multi-machine scenarios where you need to have all of the machines using the exact same tz data, we tried to make this scenario easy by letting applications quickly send the server’s tz data to the clients over the wire.  On the server, applications can serialize a TimeZoneInfo instance to a String by using the ‘ToSerializedString()’ instance method.  On the client, applications can then de-serialize the string back into a TimeZoneInfo object by calling the static method ‘TimeZoneInfo.FromSerializedString(String source)’.

  • 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.

  • 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.

  • 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.

  • 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

  • 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 :-)

  • Anders,

    Thanks for your comments on the class change.  I like the new name much better as well. :-)

  • Good to see some work being done on the TimeZone stuff. Though, I would like to add my vote to having DateTime internals being in UTC. Obviously, for historical reasons (past naivety/arrogance), this is going to be a problem. In which case I think there may be case for a new type/class branch, for use by any new, observant code.

    Anyway, as I'm feeling a little Roger Ramjety [1] this morning interesting, so: I find your use of the term "Registry Hive", um, interesting... http://msdn2.microsoft.com/en-us/library/ms724877.aspx

    [1] Roger Ramjet, the old cartoon series - protein pill - nit-pick... :)

  • Josh, thanks for the link to the .NET 2.0 code, excellent!

  • 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 ?

  • From other blog posts discussing TimeZoneInfo in .NET 3.5, it seems only the Registry Key is constant across different localized versions of Windows, so we need to use that as the time zone id.

    On another point, it seems the time zone names in Windows are just wrong! Has the whole planet missed this ? Windows says I'm in GMT, but that's only true for 6 months of the year. Right now I'm in British Summer Time (BST), but not according to time zone names in control-panel, which still say "(GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London", when I'm actually +01:00 due to daylight saving. I don't expect this to change, but the Olson names do seem more sensible, e.g. Europe/London. Then again, Cities do get renamed!

  • 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

Page 1 of 2 (21 items) 12