Why does my TIME_ZONE_INFORMATION have the wrong DST cutover date?


Public Service Announcement: Daylight Saving Time begins in most parts of the United States this weekend. Other parts of the world may change on a different day from the United States.

A customer reported that they were getting incorrect values from the GetTimeZoneInformationForYear function.

I have a program that calls GetTimeZoneInformationForYear, and it looks like it’s returning incorrect DST transition dates. For example, GetTimeZoneInformationForYear(2010, NULL, &tzi) is returning March 2nd as the tzi.DaylightDate value, instead of the Expected March 14th date. The current time zone is Pacific Time.

The value returned by GetTimeZoneInformationForYear (and GetTimeZoneInformation) is correct; you’re just reading it wrong.

As called out in the documentation for the TIME_ZONE_INFORMATION structure, the wDay field in the StandardDate and DaylightDate changes meaning depending on whether the wYear is zero or nonzero.

If the wYear is nonzero, then the wDay has its usual meaning.

But if the wYear is zero (and it is for most time zones), then the wDay encodes the week number of the cutover rather than the day number.

In other words, that 2 does not mean “March 2nd”. It means “the second week in March”.

Comments (30)
  1. SMW says:

    I just updated code that uses this function about a year ago.  The original implementation made the same mistake as given in the article.  Only semi-complex part is figuring out the proper day when wDay is 5, which means it's the last week of the month, no matter how many weeks occur in that month in the year you care about.  This same code also assumed that DST started in the calendar year before it ended.  That required a little more work but wasn't that difficult to deal with.

  2. Vilx- says:

    Ahh, date and time calculations! :) Brings back fond memories of violence and destruction. :)

    Anyway, does the above code not have a mistake then? If the year is 2010, the wDay should have specified a date, right?

  3. SMW says:

    Minor mistake on my part.  I was working with GetTimeZoneInformation and not the more recent GetTimeZoneInformationForYear function.  Working on Server 2003 machines.

  4. laonianren says:

    @SMW: Calculating the last Sunday (or whatever) of the month can be fiddly and error-prone.  Calculating the first Sunday of the next month and subtracting a week is much easier.

  5. pete.d says:

    The guy should definitely have read the docs.  No doubt about that.

    But really, that API sucks.  And there are lots of examples of that sort of thing, in Windows and other operating systems.  Drives me up the wall.  An API should be intuitive and straightforward to use.  To design it otherwise invites all sorts of bugs, and those bugs are at least partly the fault of the people who designed the API, not just those people who failed to use it correctly.

  6. James says:

    So a wDay is a Day, except on some Tuesdays, when it's a week?

  7. Crescens2k says:

    @James

    No, the MSDN example used Thursdays, so I think it would be except on some Thursdays.

    But I can see where the confusion lays as the description in the TIME_ZONE_INFORMATION structure documentation isn't that clear.

    "To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times).

    Using this notation, specify 02:00 on the first Sunday in April as follows: wHour = 2, wMonth = 4, wDayOfWeek = 0, wDay = 1. Specify 02:00 on the last Thursday in October as follows: wHour = 2, wMonth = 10, wDayOfWeek = 4, wDay = 5."

    There is also nothing in either of the functions to clarify that at all. English is my first language, but I had to read that two or three times before it actually made sense.

    [I thought it was pretty clear. It even gives two examples! Note that the documentation is written from the point of view of the person filling in the TIME_ZONE_INFORMATION structure, not the person trying to interpret the values that are already there. So much for trying to move from a declarative documentation style to the task-based documentation style that people claim to prefer. -Raymond]
  8. Joshua says:

    The concept of "register calling convention" extended to higher level APIs is always hard on the API user.

    I've seen some real doozys in Windows, but Windows is not alone.

  9. Only tangentially relevant: The government of Turkey decided to move the switchover this year by one day, from Sunday AM to Monday AM to ensure the hundreds of thousands of students who are going to take the university entrance exam do not show up late. Ooops! They seem not to have thought about small things like airline schedules and computers.

    http://www.hurriyet.com.tr/…/17238950.asp

    Stop the insanity is what I say ;-)

  10. Neil (SM) says:

    >>"To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times).

    >>Using this notation, specify 02:00 on the first Sunday in April as follows: wHour = 2, wMonth = 4, wDayOfWeek = 0, wDay = 1. Specify 02:00 on the last Thursday in October as follows: wHour = 2, wMonth = 10, wDayOfWeek = 4, wDay = 5."

    So the Monty Python team is writing MSDN documentation now?

  11. alexcohn says:

    In 1999, a bomb exploded not on time because the terrorists had out-of-date DST settings. Although it has never been confirmed that they used Microsoft software, I am no longer complaining when I must manually install special Windows update to allow the system switch to Israeli DST correctly.

  12. Timothy Byrd says:

    @Crescens2k: I never could get the hang of Thursdays…

    In the past, I've had to make a "holiday" class to specify recurring special dates. Actually, it was a struct that was supposed to be as compact as possible. "The Friday following the 4th Thursday of November" was just a beginning. I should be thankful I didn't have to encode any lunar-based holidays.

  13. Maurits says:

    @Timothy Byrd: Wot, no Easter?

  14. Cesar says:

    Since we are talking about API design, I have to leave the following pair of links here:

    The Hard To Misuse List

    ozlabs.org/…/2008-03-30.html

    ozlabs.org/…/2008-04-01.html

  15. Maurits says:

    GetTimeZoneInformationForYear

    msdn.microsoft.com/…/bb540851.aspx

    > The wYear parameter is assumed to be a local time value. If the local time is close to the transition between the old year and the new year (00:00:00 January 1), passing a UTC year to the GetTimeZoneInformationForYear function can cause the function to return time zone settings for the wrong year.

    Hmmm…

    Shouldn't GetTimeZoneInformationForYear(2012, &dtzi, &tzi); assume 2012 in *dtzi's sense*, not in the local time sense nor UTC sense?

  16. Gabe says:

    The problem with the documentation being written from the point of somebody filling out the structure is that most people will be reading the structure and trying to interpret its results. It's almost hard to imagine what kind of application would actually need to write to the structure.

  17. Voo says:

    @Ken Hagan: While it's true that dates/timezones are quite complicated and hard to get right, there's really no reason to make it harder than necessary. And values (independent of in or out) that have different meanings depending on other values are usually an extremely good warning sign for a too complicated API.

    Either make two different functions (I'd wager internally those already exist) or try to restate the problem so you don't need it – which in this case seems quite easy – you get no additional information from returning the week number instead of the day, so if you need it (and I'd wager the day is used quite a bit more often) you can get it from the day number.

    Well, too late for that and time/date APIs are just hard to get and to use right anyhow with all those peculiar internationalization problems

  18. Ken Hagan says:

    pete.d: "An API should be intuitive and straightforward to use."

    Ordinarily I'd agree, but I'm willing to make an exception for almost anything to do with dates and cultural conventions. The painful facts are that these APIs have to describe a reality that is excruciatingly unintuitive and which is typically different depending on your geographic location and which is subject to the whims of local political interference *after* you have shipped your software.

    If you want simplicity, your best bet is to impose strict UTC timestamps on your end-users and say "No, *you* sort it out.". The next easiest option is to become a megalomaniac warlord and conquer all the countries that cause API design difficulties. Trailing a sad third is learning all the ins and outs of i18n.

  19. Gabe says:

    I believe you misspelled "Mandatory National Twice-Yearly Jet Lag".

  20. Jules says:

    @Ken Hagan: sure, date handling is complex.  But it isn't made any easier by systems that put a week number in a field called "day".  

    This breaks pretty much every set of rules you'd care to mention about how to write good code — my personal favourite is for Kent Beck's rules of clean code: this is as clear a violation of #3 ("clean code […] expresses the intent of the programmers") as I've ever seen.  Sure, the decision was made to avoid introducing an extra struct (Beck's rule #4: "clean code […] minimizes the number of classes and methods"), but Beck's rules are in priority order, and this is the only way of considering such rules that makes sense.

  21. David Walker says:

    "So much for trying to move from a declarative documentation style to the task-based documentation style that people claim to prefer. -Raymond"

    Raymond, you're being a little harsh there.  It's stupid to have a parameter mean something else than what its name means, depending on the value of another parameter.  That just invites trouble.  

    We could create a function called Divide(Dividend, Divisor), where you could pass in a 1 for Divisor to have the function return the arc-tangent of the Dividend instead of dividing the two values.  Or a function that would allocate storage if the first parm is a 1, and free that storage if the first parm is a 2.  

    Even "task-based" documentation would not make calls like that any easier to work with.  The problem is not in the task-based doc, it's in the function itself.

    [Now you're changing the subject. I was responding to a complaint about the documentation, not a complaint about the interface design. I agree that the interface design is confusing. That's why MSDN include task-based documentation. -Raymond]
  22. Engywuck says:

    ah, well, and if nationwide/international complications with date/time are not enough you have to write some software where mondays start at 4AM wall-time and go until 5AM tuesday wall-time but only for one application under specific circumstances (yes, a 25-hour day. Don't ask.). And then you have to connect that program with a legacy system that does the same but won't discriminate between the first occurence of the hour "0400" till "0500" and the second one (just "monday 0443" for events) and you have to guess afterwards *which* "real" hour is meant (synced once daily between these apps by manually copying some files).

    The next step would be having that time at 0200 till 0300 causing twice a year additional havoc to the mandatory jetlag situation. We managed to avoid this. So far.

  23. David Walker says:

    OK, that's fair.  Yes, the interface design IS confusing!

  24. Crescens2k says:

    @Raymond

    I have no issues with the task based documentation. Getting into a context helps a lot because it helps people get into the right thought patterns. My issue is that it wasn't until the samples that it became fully apparant that the wDay parameter meant week of the month. I feel that "and the wDay member to indicate the occurrence of the day of the week within the month" could be written as "and the wDay member to indicate which week of the month the weekday occurs in". Because "day of the week within the month" makes me think date (ie 14th) before anything else. The bit in parenthesis adds to the confusion initially but then starts trying to clear it up at the end, and then you can get it fully cleared up with the examples.

    I don't know if it is a situation where you are very use to the API and that influences your understanding, regional differences (British person here) occuring which causes confusion or just simply the differences in the way we think but this is why i feel it was confusing.

    [It's a little awkward (but "the ordinality of the occurrence within the month" is even more awkward), I admit. (It's not the week of the month because the month may not start with a full week.) When I encountered this structure for the first time, I was able to figure out what the docs were trying to say. Maybe I'm just good at reading bad documentation. -Raymond]
  25. GregM says:

    Crescens2k, if it was described as week of the month, what would happen if that day of the week didn't occur in the first week of the month.  If the month started on Wednesday, the day of the week was Tuesday, and wDay was 2, would that be the first Tuesday of the month (the one in the second week) or the second Tuesday of the month?

  26. Crescens2k says:

    Interesting, it censored a word for no reason.

    Lets try that again, I would put (place) the first week of the month as 1st to 7th.

  27. Miral says:

    @Crescens2k:

    That's generally not how calendars work, though, and thus not how people providing data for this stuff would think.  "The first Tuesday of April" is a much more common expression than "the Tuesday of Week 2 of April".

  28. Crescens2k says:

    Thank you GregM for helping. That is exactly what I mean. It is more obvious to me what I wrote to explain things but it was unclear for you. The thing here is that does week of the month have to start at a set day and be aligned with week of the year? For me, a week is just 7 days, so I would *** the first week of the month as 1st to 7th.

  29. Maurits says:

    Interesting indeed.

    j a m jam

  30. Maurits says:

    p u t put

Comments are closed.