Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#3.5

Time Zone Lab II: Exploring the DST Adjustment Rules

3.50/5 (3 votes)
16 Jun 2016CPOL19 min read 9.7K   62  
When I needed to compute the Daylight Saving Time transitions for any year, I had to dig a bit deeper into the time zone information stored in the Windows Registry.

Introduction

To test some boundary cases in a program that reads the time stamps attached to the files stored on a Windows computer, I needed ready access to the Daylight Saving Time (DST) transition dates for any year.

Background

Two years ago, to meet a similar requirement, I wrote a program that I called my Time Zone Lab, about which I wrote in "Time Zone Lab: An Expedition into Windows Time Zones," at http://www.codeproject.com/Articles/816165/Time-Zone-Lab-An-Expedition-into-Windows-Time-Zone.  Today, to meet a closely related, need, I concluded that the simplest way to extract the information that I needed from the time zone tables in the Windows Registry was to extend the Time Zone Lab, then construct a Microsoft Excel worksheet around the output of the new routine.

Since most parts of the world observe Daylight Saving Time (hencforeth referred to as "DST"), and they will never be able to agree on when to start and stop using DST, thanks to the way Earth's axis it tilted in relation to the Sun, Windows keeps a set of adjustment rules for each of the world's time zones. To further complicate matters, from time to time, countries change the rules that govern when DST starts and ends, as the United States did in 2007. Hence, there is not one rule, but a set of them, for each time zone, each having a starting and ending date that determines the years to which it applies. As an example, the Central Standard Time zone, in which I live, has two rules. The first rule applies to all dates through the end of 2006, and the second applies to everyth8ing since then.

Long before I dug into it, I wondered whether Windows kept a table of years containing a row for each year that held the DST start and end dates for that year for each time zone. However, such a table would be gigantic, and it would impose a perpetual maintenance burden on Microsoft that can be easily avoided, since the transition dates are based on mathematical formulas, making it relatively straightforward to compute the transition dates for any year as needed.

Using the code

Extracting the adjustment rules falls to a new member of static class TimeZoneTasks. The new method, EmumerateTimeZoneAdjustments, has two methods of determining the time zone for which to extract the adjustment rules. 

  1. When the program is called with an initial command line argument of EmumerateTimeZoneAdjustments, followed by the ID of a time zone, the adjustment rules that apply to the specified time zone are reported.
  2. When the program is called without arguments, the adjustment rules that apply to the local time zone, which is determined by the Regional Settings area of the Windows Control Panel, is reported.

If you don't happen to know them by heart, the EnumTimeZones command line option delivers a complete list of the time zone IDs, along with a great deal of information about each. Since this task creates a very wide table, you may want to pipe the output into a text file, so that you don't have to unwrap the very long lines that cover the basics of each time zone. Somewhat to my surprise, the Time Zone ID is not some fabricated, otherwise meaningless code; in most cases, it is the name by which the time zone is known in the country for which the computer is configured.

If you would rather avoid dealing with a console program that creates wide reports, you can find the output that was generated by that report from the computer on which the original Time Zone Lab was created in worksheet Time Zones on ZAPHOD42 of Microsoft Excel workbook DST_Transition_Date_Computations.XLSX, about which more shall be said shortly. For those who are just itching to know, ZAPHOD42 is the NETBIOS name I gave to that computer, which is now mostly relegated to supporting a DOS application that remains under my occasional care. This year, my work has moved to a spiffy new Windows 7 machine that runs a 64 bit operating system, and is assigned a NETBIOS name of ENIGMA.

The table of adjustment rules is exposed to .NET programs through the TimeZoneInfo.AdjustmentRule object, in particular, its TransitionTime property, which defines the parameters from which the adjustment dates for any year covered by the rule are derived.

The TimeZoneInfo.AdjustmentRule Object

TimeZoneInfo.AdjustmentRule object exposes the following five properties that fully describe one DST adjustment rule.

  1. DateStart is a System.DateTime structure that identifies the earliest date to which the rule applies. Although the property is a complete DateTime structure, only the date portion is used; the members that represent times are set to zero.
  2. DateEnd is another System.DateTime structure that identifies the last date to which the rule applies. The observation made above about the DateStart member applies to this member, too.
  3. DaylightDelta is a System.TimeSpan structure that gives the amount of time that is added to the standard time to derive the DST time. The Days member is always zero, and I suspect it is safe to say that so are the Seconds amd Milliseconds members. Some regions may use the Minutes member to adjust by a fraction of an hour, although I did not scan for such cases. Regardless, the Ticks member is the number of Ticks (100 nanosecond increments) that is equal to the sum of everything else, converted to ticks.

  4. DaylightTransitionStart is a TransitionTime structure that defines the parameters required to determine when DST starts.

  5. DaylightTransitionEnd is a TransitionTime structure that defines the parameters required to determine when DST ends.

The TransitionTime Structure

Just as a TimeZoneInfo.AdjustmentRule object exposes five properties, so does a TransitionTime structure. I suspect the reason for this being a structure, rather than a full-fledged class, is that the members are read directly from the binary TZI value in the Windows Registry. Unlike the other adjustment rule members, nothing needs transformation.

  1. IsFixedDateRule is a Boolean value that, if true, means that the transition occurs on a fixed date, such as the first day of November. Otherwise, the transition occurs on a floating date that is computed from the values of the other properties.

  2. Month is the ordinal number of the month in which the transition occurs. This propertiy applies to all transition dates. Valid values are 1 through 12, the month numbers to which you are almost certainly accustomed,, which coincidentally correspond to the values found in the wMonth member of the SYSTEMTIME structure used by the underlying Windows API routines. This member is a System.Int32.

  3. Day is the ordinal day of the month on which the transition occurs. Its value applies only to transitions that occur on a fixed date, that is, when IsFixedDateRule is true. When IsFixedDateRule is false, its value is 1, which is ignored.  This member is a System.Int32.

  4. DayOfWeek expresses the weekday on which the transition occurs as a member of the System.DayOfWeek enumeration. These names are symbolic, and are not localized, meaning that you may see Sunday, even though you know it as Sontag, the German name for Sunday, but this is no different than other enumerated types, and they thoughfully provided instructions for getting a localized name, right in the documentatin page, https://msdn.microsoft.com/en-us/library/system.timezoneinfo.transitiontime.dayofweek(v=vs.110).aspx. You should be aware that the numerical values are from zero for Sunday through 6 for Saturday, which happens to correspond to the values found in the wDayOfWeek member of the SYSTEMTIME structure used by the underlying Windows API routines. This member plays a role when the IsFixedDateRule  member is false, which I suspect is most of the time.

  5. Week is the week of the month in which a time change occurs, ranging in value from 1 to 5, where 5 means the last weekday of the specified month, and applicable only to floating dates, that is, those for which the IsFixedDateRule member value is false. This member is a System.Int32.

Though I could have had this method generate a table of transition dates for a span of years or calculate them for a specified year, I chose to put that work into the Microsoft Excel workbook that already held the aforementioned time zone chart. The next section discusses the worksheet, and explains its formulas.

Microsoft Excel Workbook DST_Transition_Date_Computations.XLSX

The most relevant, not to mention interesting, worksheet is CST Adjustment Rules, which is divided into three major sections, with three minor sections that play supporting roles.

  1. The entire worksheet revolves around the section near the top, composed of 25 columns divided into four color coded groups. This table stores everything in the two adjustment rules stored in my computer for the Central Standard Time zone in which I live.
    1. The first part, with the blue headings, gives the index (array subscript), ordinal, and effective dates of a DST adjustment rule. The effective dates correspond to the DateStart and DateEnd properties of the TimeZoneInfo.AdjustmentRule from which it was populated.
    2. The columns in the pink region display the relevant members of the TimeSpan structure that holds the time adjustment delta, corresponding to the DaylightDelta  member from which the values originated.
    3. The columns that have green headings and borders correspond to the DaylightTransitionStart structure, a TimeZoneInfo.TransitionTime structure, in case you forgot; everyting in it is displayed, augmented by lookups of the weekday and month names from two small tables located past the right end of the entire table. These tables comprise two of the three supporting actors mentioned above.
    4. The columns with tan headings and brown borders correspond to another TransitionTime structure, DaylightTransitionEnd. 
  2. Immediately beneath the blue columns described above is another distinct region in which the cells in all but the last row are blue. Since it depends on the first area, it was created next, and was used to work out the formulas that went into the last major section.
    1. Since this area is devoted to computing the transition dates for a single year, the only input variable is cell B20, which corresponds to a one-cell named range, DST_Year.
    2. The cells to the right use the values in the blue cells above them to compute and display the transition dates.
    3. The blue cells are populated from the the applicable row in the multi-colored table above based on the transition rule that applies to the specified year.
    4. The formula in the cell to the left of the year, cell A20, determines the correct adjustment rule to apply by range testing the year in cell A20 against both rows in the table of adjustment rules. Since I was iterested in just one time zone, which has just two rules, I didn't bother to figure out how to range test against an infinite number of rows. Should it be necessary, that is left as an exercise for the first reader who needs it.
    5. The ten blue cells above the two transition dates are one-cell named ranges, from which I constructed their formulas. I use this technique when I want or need to make the parameters that went into a formula clear to an audience, which might be me a year ot two from now.
  3. The final major section is a table of transition dates from 1999 through 2400, which happens to be the next leap century.
    1. The columns that have purple headings and borders contain the years and transition dates.
    2. The remaining columns display the values looked up from the adjustment rule tables, from which the adjustment dates are derived.
    3. Except for the column that stores the IsFixedDateRule value, the column names in ghe light gray section corresond exactly to those of the columns in the table of adjustment rules from which their values are drawn. To avoid further widening of the columns, I shortened those labels to Fixed, which should convey what they are.
    4. Between the row that contains the column group labels and the row that contains the individual column labels is an unlabeled row that contains numbers; these are the column numbers from which the data in that column are drawn from the table of adjustment rules. The lookup formulas use the values in these cells, so that the formula in the upper left corner can be copied into the rest of the table. Otherwise, the cell formulas are garden variety VLOOKUP formulas that use addrresses anchored to the column that stores the Index of the adjustment rule (column G) and the row (row 23) that stores the column of the Adjustment_Rules_Lookup range from which to draw the value for the cell.
    5. The formulas in in the transition date cells in the purpose area were developed by copying the two formulas that were constructed with named ranges into a text editor, then using its Find and Replace function to replace each named range with the appropriate cell from the top row in the region of light gray data cells to their right. Once tested, these two formulas were copied down 402 times to complete the table.

There are three key formulas that warrant explanation, the first of which is the one in column G, which determines the rule upon which everything to its right is based by performing two inclusive range tests. At first glance, it looks like a monster, and it is on the long side.

=IF(AND(D25>=YEAR(CST_Adjustment_Rule_1_Start_Date),D25<=YEAR(CST_Adjustment_Rule_1_End_Date)),0,IF(AND(D25>=YEAR(CST_Adjustment_Rule_2_Start_Date),D25<=YEAR(CST_Adjustment_Rule_2_End_Date)),1,"Undefined"))

This formula is easier to understand when it is formatted more like a nested method call, as follows.

<code>=IF ( AND ( D25 >= YEAR ( CST_Adjustment_Rule_1_Start_Date ) ,</code>
<code>            D25 <= YEAR ( CST_Adjustment_Rule_1_End_Date ) ) ,</code>
<code>      0 ,</code>
<code>      IF ( AND ( D25 >= YEAR ( CST_Adjustment_Rule_2_Start_Date ) ,</code>
<code>                 D25 <=YEAR ( CST_Adjustment_Rule_2_End_Date ) ) ,</code>
<code>                 1 ,</code>
<code>                 "Undefined"</code>
<code>         )</code>
<code>    )</code>

The formulas that calculate the transition year are easier to understand if you start by wrapping your head around the fromulas in cells C20 and D20, which are written with named ranges.

The formula in cell C20 calculates the DST transition start date (the date on which observation of DST begins) is as follows.

=IF(DST_Start_IsFixedDate,DATE(DST_Year,DST_Start_Month,DST_Start_DayOfMonth),DATE(DST_Year,DST_Start_Month,(DST_Start_Week*7+1)-WEEKDAY(DATE(DST_Year,DST_Start_Month,8-DAY(DST_Start_Weekday)))))

I'll parse this one out like I did the adjustment rule lookup.

<code>=IF ( DST_Start_IsFixedDate ,</code>
<code>      DATE ( DST_Year ,</code>
<code>             DST_Start_Month ,</code>
<code>             DST_Start_DayOfMonth ) ,</code>
<code>      DATE ( DST_Year ,</code>
<code>             DST_Start_Month ,</code>
<code>             ( DST_Start_Week * 7 + 1 )</code>
<code>             - WEEKDAY ( DATE ( DST_Year ,</code>
<code>                                DST_Start_Month ,</code>
<code>                                8 - DST_Start_Weekday </code>
<code>                              )</code>
<code>                       )</code>
<code>             )</code>
<code>      )</code>

The outermost IF function evaluates a Boolean, DST_Start_IsFixedDate.

  • Since a value of True means that the transition occurs on a fixed day each year, computation of which is straightforward.
  • Computation of a floating date is considerably more complex, and is easier to understand if it is broken down a bit further.
    • Computing the year and month components of the date is identical for both fixed and floating dates. The formula could be simplified by moving the IF function into the third argument of a single Date function. However, since the formula as written is tested, meets my immediate need, and is not noticeably inefficient, I shall leave simplification as an exercise for another colleague who cherishes computational efficiency as much as do I.
    • The Day portion of the floating date formula divides neatly into two halves, which I shall diagram separately.
    • The first half is DST_Start_Week * 7 + 1, which gives the day of the month on which the Sunday following the transition falls, keeping in mind that both Windows and Excel start their weeks on Sunday, and that DST_Start_Week values range from 1, for the first week of a month, to 5, which represents the last week of a month, which is usually a partial week.
    • From this value, the Weekday on which the transition occurs, which is an integer value between 1 (Sunday) and 7 (Saturday) is subtracted.
    • Since the Weekday function expects a Date as input, a nested Date function supplies it. 
    • The Year and month of the final nested Date function are as above, while the Day is 8 less DST_Start_Weekday. Think this through; since Sunday is 1, Day resolves to 7 (8 - 1). Hence, for the year 2016 in the Central Standard Time zone,, the formula is Date (2016,3,15)-Weekday(Date(2016,3,7)), which simplifies to Date (2016,3,15)-2, or Sunday, 13 March 2016.

Computation of the ending transition date is analogous, substituting the _End_ rnage names for the _Start_ ranges.

The formulas in the table follow the same design, replacing the range names with relative column addresses, with the caveat that the final subexpression, e. g., N25+1 in cell E25, must be enclosed in parentheses to force the correct evaluation order.

For the sake of completeness, the third of the three bit players in this worksheet is the legend in the upper left corner.

I don't expect anybody to use the 402 year table, which I created as a mental exercise. I expect to actually use the year field in the blue region, which requires less work; enter a year, get transition dates.

To prevent accidents that would render it unusable, the workseet is protected. The password, TheCodeProject, is no big secret; I put a copy in the comments section of the workbook's advanced properties. The year cell in the transition date calculation area is open, as are the adjustmenr rule cells in the top table, so that you can input the values for the time zone of interest to you.

I cannot leave the workbook without calling attention to a copule of its other interesting features.

  • _Index of Named Ranges is a standard worksheet that I keep in my library of Excel templates, from which I can quickly insert it into any maderately complex project that uses many ranges, such as this one. You fill it by placing the insertion point in cell A2 and selecting Paste Names, the last choice on the Use in Formula pulldown on the Formulas ribbon. This command fills the cells in columns A and B, while formulas fill the remaining columns. If additional ranges are pasted below the bordered cells, do the following.
    • Copy the formats from the last bordered row of columns A and B down as far as necessary, or maybe a bit further, to allow room for further growth. Use Paste Special for this task.
    • Copy everything from the last bordered row of columsn C through H down as far as you copied the formats in columns A and B. Since you want everything, this is a regular copy operation.
  • Index is a directory of the entire workbook, which makes all of its worksheets readily accessible without usintg the clumsy VCR buttons that Microsoft provided at the bottom of the window. This worksheet is generated by a VBA macro in an add-in, which can be run as often as necessary to update the index. When you run the macro, the index is completely rebuilt, then sorted by worksheet name. If you insert another sheet in front of it, not to worry; the next time you run the macro, a brand new sheet is inserted at the very front of the tab order. The add-in, WorkbookIndexGenerator.XLAM, is included, along with DST_Transition_Date_Computations.XLSX, in the NOTES directory of the archive that accompanies this article. The VBA project in the add-in is signed with a code signing certificate that was valid when I signed it.

I usually manually color code the first two tabs as shown; the macros don't do this, and I've never bothered to fully automate maintenance of the range name index. Once upon a time, I had a macro that did everything, but I found that it wa too combersome, and abandoned it long ago.

Points of Interest

Shifting the focus back to the C# code, while most of the code is unremarkable (especially the new work), there are a couple of noteworthy features of the new code, and several in the esisting code that are worth your attention.

Rearranging Output Generated by string.Format

The ShowTransitionTimeDetails method began as a garden variety string formatter, extracted into its own one-statement method, since the same statement is needed twice to report the complete contents of an adjustment rule, each of which contains two TransitionTime structures.

C#
private static string ShowTransitionTimeDetails ( TimeZoneInfo.TransitionTime ptzTransitionTime )
{
    return string.Format (
        "IsFixedDateRule = {0}, Month = {3}, Week = {5}, Day = {1} ({2}), TimeOfDay = {4}" ,
        new object [ ]
        {
            ptzTransitionTime.IsFixedDateRule ,                        // Format Item 0 = IsFixedDateRule
            ptzTransitionTime.Day ,                                    // Format Item 1 = Day
            ptzTransitionTime.DayOfWeek ,                            // Format Item 2 = DayOfWeek
            ptzTransitionTime.Month ,                                // Format Item 3 = Month
            ptzTransitionTime.TimeOfDay ,                            // Format Item 4 = TimeOfDay
            ptzTransitionTime.Week                                    // Format Item 5 = Week
        } );
}    // ShowTransitionTimeDetails

Its noteworthy feature is that the current format control string is a revision that displays the fields in a different order than did the original. However, since format items are matched with elements in the parameter array by their positions (subscripts), the array was unaffected. This is a welcome improvement over the limitations imposed by the printf functions in the C runtime library, Indeed, it makes me wonder whether anybody has considered porting string.Format to C.

Robust Command Line Argument Parsing

Almost every Windows program I have ever created, even many that have graphical user interfaces, and run in the Windows subsystem, accept command line arguments. While this is hardly unusual, what might be is the work that I have put into the robust command line parsing engine that I use.  The next figure shows the method call that instantiates the command line engine for the time zone lab.

C#
//  ----------------------------------------------------------------
//    After I created the first version of this assembly, I realized
//    that this array is used once only, and may as well be built when
//    it is needed, and discarded when the CmdLneArgsBasic constructor
//    returns.
//  ----------------------------------------------------------------

CmdLneArgsBasic cmdArgs = new CmdLneArgsBasic (
    new char [ ] { SW_OUTPUT } ,                                    // This program supports one switch argument, which is specified by way of a disposable single-element array.
    CmdLneArgsBasic.ArgMatching.CaseInsensitive );                    // Use the Case Insensitive parsing rules.
cmdArgs.AllowEmptyStringAsDefault = CmdLneArgsBasic.BLANK_AS_DEFAULT_ALLOWED;    // This property cannot be set by the constructor.

Since this assembly has a narrow focus and few options, it makes few demands on the command parsing engine. The constructor defines one switch, SW_OUTPUT, and specifies that argument matching shall be case insensitive.  The remaining arguments are positional. Since a CmdLneArgsBasic object supports up to nine of these by default, nothing further is required to process the maximum of two that it needs. The CmdLneArgsBasic class is one of a number exported by WizardWrx.DLLServices2.dll, which I recently published as an open source repository on GetHub. The repository URL is https://github.com/txwizard/DLLServices2, The library is available under a three-clause BSD license, and I would be delighted to have some extra hands to extend it in several ways. Since its dependence upon three 32 bit native functions limits the library to 32 bit address spaces, I think the most valuable feature that the library needs is set forth in the first issue opened against the project (by me), which you can review at https://github.com/txwizard/DLLServices2/issues/1.

Suppressing the Banner

Although this assembly doesn't really need it, since its development started from a template into which it was already incorporated, it gets a feature that enables it to suppress part or all of its console output. Since the routine that makes this decision might need to create a warning message that need not be displayed until after the banner, it returns the message through an out parameter of type string. The block of code shown below processes this command line option, which happens to be the only supported switch. Depending on the outcome, it displays the banner if the output option allows it. Next, if SetOutputFormat left a message in its out paramter, the message is displayed. Finally, the block that began with the definintion of the output string closes, allowing the string to go out of scope, and be garbage collected. Since the OutputFormat must outilive this block, it is defined and initialized just before execution enters the block.

C#
OutputFormat enmOutputFormat = OutputFormat.None;                    // enmOutputFormat needs method scope, but initialization happens inside a scope block.
{    // Confine the scope of strDeferredMessage without extracting this little block into a separate method.
    string strDeferredMessage = null;

    //  ----------------------------------------------------------------
    //    Short of returning arrays of objects, there is no way for a
    //    method to return two or more values. Since the value that is of
    //    primary interest is the OutputFormat, while strDeferredMessage
    //    goes unused most of the time, since it is reserved for reporting
    //    exceptional circumstances that can wait until after the banner
    //    is displayed, it plays second fiddle. For a method, this means
    //    that it becomes an Out parameter. Since strings are immutable,
    //    and the object is to let SetOutputFormat create a message to be
    //    displayed later, the parameter must be an Out parameter, rather
    //    than a conventional object reference.
    //  ----------------------------------------------------------------

    enmOutputFormat = SetOutputFormat (
        cmdArgs ,
        ref strDeferredMessage );

    if ( enmOutputFormat != OutputFormat.None )
    {   // Unless output is suppressed, display the standard BOJ message.
        s_theApp.DisplayBOJMessage ( );
    }   // if ( enmOutputFormat != OutputFormat.None )

    if ( !string.IsNullOrEmpty ( strDeferredMessage ) )
    {   // SetOutputFormat saves its error message, if any, in SetOutputFormat.
        Console.WriteLine ( strDeferredMessage );
    }   // if ( !string.IsNullOrEmpty ( s_strDeferredMessage ) )
}    // String strDeferredMessage goes out of scope, allowing any memory that it appropriated to be reclaimed.

The SetOutputFormat method parses its string input into a nice, compact enumerated type that is much more efficient to process and pass around.

C#
private static OutputFormat SetOutputFormat (
    CmdLneArgsBasic pcmdArgs ,
    ref string pstrDeferredMessage )
{
    //  ----------------------------------------------------------------
    //  An invalid input value elicits a message similar to the following.
    //
    //      Requested value 'Foolish' was not found.
    //
    //  The simplest way to report an invalid value is by extracting it
    //  from the Message property of the ArgumentException thrown by the
    //  Enum.Parse method.
    //
    //  I happen to have a library routine, ExtractBoundedSubstrings,
    //  which became part of a sealed class, WizardWrx.StringTricks,
    //  exported by class library WizardWrx.SharedUtl2.dll version 2.62,
    //  which came into being exactly two years ago, 2011/11/23.
    //  ----------------------------------------------------------------

    const bool IGNORE_CASE = true;
    const int NONE = 0;

    OutputFormat renmOutputFormat = OutputFormat.Verbose;

    //  ----------------------------------------------------------------
    //  Enum.Parse needs a try/catch block, because an invalid SW_OUTPUT
    //  value raises an exception that can be gracefully handled without
    //  killing the program.
    //  ----------------------------------------------------------------

    try
    {
        if ( pcmdArgs.ValidSwitchesInCmdLine > NONE )
        {
            renmOutputFormat = ( OutputFormat ) Enum.Parse (
                typeof ( OutputFormat ) ,
                pcmdArgs.GetSwitchByName (
                    SW_OUTPUT ,
                    OutputFormat.Verbose.ToString ( ) ) ,
                IGNORE_CASE );
        }   // if ( pcmdArgs.ValidSwitchesInCmdLine > NONE )
    }
    catch ( ArgumentException exArg )
    {   // Display of the message is deferred until the BOJ message is printed.
        s_theApp.BaseStateManager.AppExceptionLogger.ReportException ( exArg );

        pstrDeferredMessage = string.Format (
            Properties.Resources.ERRMSG_INVALID_OUTPUT_FORMAT ,
            WizardWrx.StringTricks.ExtractBoundedSubstrings (
                exArg.Message ,
                SpecialCharacters.SINGLE_QUOTE ) ,
            renmOutputFormat ,
            Environment.NewLine );
    }

    return renmOutputFormat;
}   // private static OutputFormat SetOutputFormat

I prefer enumerations over strings for use within switch blocks for two reasons.

  1. Enumerations are extremely efficient to process. I have seen debug code that implemented a switch statement as a jump table. It doesn't get any better than that!
  2. Strings for use in a switch block musst be either literals hard coded into the statement or constants. Resource strings are not considered constants, and cannot be used.

There is probably at least another nugget or two that I've overlooked. I have been told that reading my code is a lot like peeling an onino, but without the tears. Enjoy!

History

Thursday, 16 June 2016 is the first version of this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)