Home > Articles > Programming > Windows Programming

This chapter is from the book

Let's Discover Something

Let's rejoin our eXtreme .NET team to see how they are learning about spiking.

Exercise 6-1: Spiking How Time Zone Data Works in Windows

In this exercise, we are going to investigate how we can use time zone information in our application. In Chapter 3 we encountered this as a task we couldn't accurately estimate. We didn't know enough about how time zones are stored to give an accurate estimate. We need to understand how to access the time zone data from the operating system. Then we need to work out how to use this information to get the time in different places in the world. With this knowledge, we can go back to our team with some firmer estimates about how long this task will take to develop.

The first place to look is the classes that ship as part of the .NET Framework. If there is support for time zone data there, our lives will be made much simpler.

  1. Open up the help file and look up the .NET Framework DateTime structure.

    Unfortunately the only time zone–related functionality provided is to convert local times to time in UTC (formally GMT; don't ask me why this was changed!) and back again. This just works using the local time zone that the machine is currently running in. As the documentation states:

    This method assumes that the current DateTime holds the local time value, and not a UTC time. Therefore, each time it is run, the current method performs the necessary modifications on the DateTime to derive the UTC time, whether the current DateTime holds the local time or not.

    This method always uses the local time zone when making calculations. (Source: MSDN library)

  2. Within the System namespace, there is a TimeZone class; let's see whether that is useful for us.

    All the documentation has to say about it is this:

    A time zone is a geographical region in which the same standard time is used. (Source: MSDN library)

    Not very verbose, but it sounds as though it might be promising.

    If you examine the methods, you will discover that the constructor is protected, and the only way to create an instance of this class is to use the static property CurrentTimeZone. This is not very useful if we want to get the time zone information for different time zones.

    (As another exercise, you can try to build a test application using the Time-Zone type and see whether you can get time zone for some other part of the world.)

  3. Our next stop is to see what the Win32 API provides in terms of time zone functionality. A couple of functions look interesting: GetTimeZoneInformation and SetTimeZoneInformation. They both work with a TIME_ZONE_INFORMATION structure, which contains all the data that we need to build our application; for a given time zone, the fields in the structure are as shown in the following table.

    Time Zone Fields

    Field

    Description

    Bias

    The offset from UTC in minutes

    Standard Name

    The name of standard time on this machine in this time zone

    Standard Date

    The date and time that daylight savings moves over to standard time

    Standard Bias

    The additional difference from UTC during standard time (usually zero)

    DaylightName

    The name of daylight savings time on this machine in this time zone

    DaylightDate

    The date and time of the transition from standard time to daylight savings time

    DaylightBias

    The additional difference from UTC during daylight savings time (usually–60)

    This looks promising, but the methods only enable you to get or set the time zone on the local machine and retrieve time zone information about the time zone set on the local machine.

    We are still stumped. We want to get time zone information for all the available time zones in the world. We figure it must be possible because the Control Panel extension that you use to set the Date and Time does it in the Time Zone tab.

  4. That is the next place for us to look. If we could work out how the timedate.cpl (basically a DLL) worked, we could emulate that and get all the time zone information for around the world. So, first, we can look to see whether it exposes any form of interface (COM or .NET). You can try to open it using OLE View or ildasm, but no luck there; it supports neither a COM interface nor does it contain CLR header information.

  5. Back to basics, open the CPL in Visual Studio as a resource to examine whether there are any clues in there. Maybe the time zone information might have been stored in the DLL (maybe in a string table).

    Nope.

    The only thing left we can think of is to open up the timedate.cpl file in a hex editor. (Yuck, I haven't poked around inside Windows system files for a while; surely there's a good reason for that!)

    We begin this stage of the investigation by examining the DLLs that the code uses, all the usual suspects: KERNEL32, NTDLL.DLL, USER32.dll, COMCTL32.dll, ole32.dll, SHELL32.dll, GDI32.dll, ADVAPI32.dll, IMM32.dll and SHLWAPI.dll. Nothing of use here.

    So carry on looking through the file for any other obvious clues; maybe the time zone data is hard coded in the DLL. Again no.

    So where did the time zone information get stored? How about the Registry? Look for any references to Registry paths in the CPL file; you should find a few. The first ones lead to information about time synchronization via a timeserver and then some Registry entries for storing the current time zone settings for the local machine. Then about halfway down the file you can find a reference to the Registry key: Software\Microsoft\Windows NT\CurrentVersion\Time Zones.

    Eureka!

  6. This key contains a subkey for each time zone that is listed in the combo box in the second tab of the timedate.cpl. The data in each of the subkeys has to provide all the data required to build a TIME_ZONE_INFORMATION structure so that the application can set the time zone for the system. Now we just have work out how the data is formatted.

    Registry Information for Each Time Zone

    Value

    Meaning

    Display

    Extra information about the time zone for display purposes

    Dlt

    DaylightName; the name of daylight savings time on this machine

    Index

    Unknown

    MapID

    Presumably used for placing on the bitmap of the world in the CPL

    Std

    StandardName; the name of standard time on this machine

    TZI

    All the other data we need to fill a TIME_ZONE_INFORMATION structure

  7. The string data is reasonably clear, as shown in the preceding table; we just need to work out how the byte data in the TZI (Time Zone Information) is stored. We start off by going back to the documentation on the structure, from which we can draw up the following table. So adding up the numbers, we are looking for 44 bytes of information, and the TZI value in the Registry is exactly 44 bytes long. Therefore, we must assume that we have all the data we need in the byte array. All we need to do is figure out which byte means what!

    Break Down of Time Zone Information

    Data Type

    Name

    Description

    Bytes

    LONG

    Bias

    In minutes

    4

    SYSTEMTIME

    StandardDate

    Month, day of week and day only

    16

    LONG

    StandardBias

    Mostly 0 (minutes)

    4

    SYSTEMTIME

    DaylightDate

    Month, day of week and day only

    16

    LONG

    DaylightBias

    Mostly –60 (minutes)

    4

  8. On your own, write a program that decodes the byte data in the TZI fields in the Registry, and try to work out how the time zone information data is stored in the TZI field. The best thing to do would be to write some tests that validate how we believe the time zone data is stored. We can run those tests when the OS changes and check whether the way time zone data is stored has changed.

    You should have deduced the data was stored in the following order: Bias, Standard Bias, Daylight Bias, Standard Date, and Daylight Date. Okay, now we are ready to put together some code to use this information.

    Below are some functions written in C# that use the time zone information from the Registry.

  9. To populate a list with the available time zones, a static method on a class is shown first. It is static because it requires no instance data to be functional.

    public static string[] GetTimeZones()
    {
        RegistryKey regKey = Registry.LocalMachine;
        regKey = regKey.OpenSubKey(
            @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\");
        return regKey.GetSubKeyNames();
    }
    

GetTimeZones Function

After a time zone has been selected, you must get the time zone information for the place selected. The next set of code demonstrates this.

  1. Open the Registry key for the time zone provided.

  2. Read in the TZI value to a byte array.

  3. Use some helper functions to extract the data out of the byte array and into the member variables of the PlaceTime object.

The helper functions MakeUShort and MakeInt are equivalent to the traditional Windows MAKEWORD and MAKELONG macros. Remember that a Long in .NET world is 64 bits and not 32 bits as it was in the old Win32 world. GetValueFromBytes builds a 32-bit integer value from an array of bytes, using an offset from the beginning of the array at which to start "stripping" the bytes.

protected void GetTimeZoneInfo(string strTimeZone)
{
    RegistryKey regKey = Registry.LocalMachine;
    regKey = regKey.OpenSubKey(
        @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" +
            strTimeZone);
    System.Byte[] tziData = (Byte[])regKey.GetValue("TZI");

    m_nBias = GetValueFromBytes(tziData, 0);
    m_nStandardBias = GetValueFromBytes(tziData, 4);
    m_nDaylightBias = GetValueFromBytes(tziData, 8);

    m_StandardDate = GetDateTimeFromBytes(tziData, 12);
    m_DaylightDate = GetDateTimeFromBytes(tziData, 28);
}


private static ushort MakeUShort(byte low, byte high)
{
    return (ushort) (low | (high<< 8));
}

private static int MakeInt(ushort low, ushort high)
{
    return (int) (low | (high<< 16));
}

private static int GetValueFromBytes(byte[] data, int offset)
{
    int nResult = 0;
    ushort lowWord = MakeUShort(data[offset],data[offset+1]);
    ushort highWord = MakeUShort(data[offset+2],data[offset+3]);
    nResult = MakeInt( lowWord, highWord );
    return nResult;
}

private static DateTime GetDateTimeFromBytes(byte[] data, int offset)
{

    DateTime dateTime = DateTime.UtcNow;

    int Year = MakeUShort(data[offset],data[offset+1]);
    int Month = MakeUShort(data[offset+2],data[offset+3]);
    DayOfWeek DayofWeek =
         (DayOfWeek)MakeUShort(data[offset+4],data[offset+5]);
    int Day = MakeUShort(data[offset+6],data[offset+7]);
    int Hour = MakeUShort(data[offset+8],data[offset+9]);
    int Minute = MakeUShort(data[offset+10],data[offset+11]);
    int Second = MakeUShort(data[offset+12],data[offset+13]);
    int Milliseconds = MakeUShort(data[offset+14],data[offset+15]);

    dateTime = dateTime.AddMonths(Month - dateTime.Month);
    dateTime = dateTime.AddDays(1-dateTime.Day);
    dateTime = dateTime.AddHours(Hour - dateTime.Hour);
    dateTime = dateTime.AddMinutes(Minute - dateTime.Minute);
    dateTime = dateTime.AddSeconds(Second - dateTime.Second );
    dateTime = dateTime.AddMilliseconds(Milliseconds –
        dateTime.Millisecond );
    bool bFoundDay = false;
    int nCoveredRequiredDay = 0;
    while (dateTime.Month == Month)
    {
        if (dateTime.DayOfWeek == DayofWeek)
        {
            nCoveredRequiredDay +=1;
            if (nCoveredRequiredDay == Day)
            {
                bFoundDay = true;
                break;
            }
        }
        dateTime = dateTime.AddDays(1);
    }
    while(!bFoundDay)
    {
        dateTime = dateTime.AddDays(-1);
        if (dateTime.DayOfWeek == DayofWeek)
        {
            bFoundDay = true;
        }
    }
    return dateTime;
}

GetTimeZoneInfo and Related Functions

The final helper function, GetDateTimeFromBytes, builds a .NET Framework Date-Time structure from the byte array passed in, starting at the offset provided. This is the more complicated of the methods here and requires some extra explanation.

The TZI byte array contains 2 sets of 16 bytes, which correspond to a Win32 SYSTEMTIME structure. These SYSTEMTIMEs contain the data for the Standard Date and Daylight Date in the TIME_ZONE_INFORMATION structure. The standard and daylight dates represent the date and time at which the transition occurs from daylight savings to standard time and back again.

The TIME_ZONE_INFORMATION structures contain the month, the day of the week (for example, Sunday), the day on which the transition occurs, along with the time. The day can be a value between 1 and 5. If the month is 4, the day is 1, and the day of the week is 0 (Sunday), that would represent the first Sunday in April. If the day value is 5, that always means the last of that day in the month. If the month is 9, the day is 5, and the day of the week is 6, which represents the last Saturday in September.

The GetDateTimeFromBytes function sets the DateTime structure to the first day of the month given and walks through the month until it finds the correct day of the transition. If the method fails to do this, it assumes (dangerous, I know) that we want the last occurrence of that day of the week in the month and starts walking backward through the month until it finds that day. I have no doubt this could be greatly optimized, but I leave that as another exercise for you!

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020