{"id":9193,"date":"2011-11-04T07:00:00","date_gmt":"2011-11-04T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/11\/04\/how-do-i-generate-a-unique-32-bit-value-for-a-time-zone\/"},"modified":"2011-11-04T07:00:00","modified_gmt":"2011-11-04T07:00:00","slug":"how-do-i-generate-a-unique-32-bit-value-for-a-time-zone","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20111104-00\/?p=9193","title":{"rendered":"How do I generate a unique 32-bit value for a time zone?"},"content":{"rendered":"<p>\nPublic Service Announcement:\nDaylight Saving Time ends in most parts of the United States this weekend.\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/11\/05\/10086404.aspx#10086843\">\nOther parts of the world may change on a different day from the\nUnited States<\/a>.\n<\/p>\n<p>\nA customer asked the following question:\n<\/p>\n<blockquote CLASS=\"q\"><p>\nGiven two\n<code>TIME_ZONE_INFORMATION<\/code> structures,\nI would like to compute a <code>LONG<\/code> for each\nthat I can then compare to determine whether they represent\nthe same time zone.\n When I say the same, I mean that when the two are passed\nto <code>System&shy;Time&shy;To&shy;Tz&shy;Specific&shy;Local&shy;Time<\/code>\nwith the same <code>LPSYSTEM&shy;TIME<\/code> input, the output is the same.\n<\/p><\/blockquote>\n<p>\nA <code>TIME_ZONE_INFORMATION<\/code> structure\ncontains more information than can be packed into a 32-bit value.\n(At least there&#8217;s no obvious way to pack it into a 32-bit value.)\nYou&#8217;re not going to be able to squeeze the entire structure\ninto a 32-bit value that is unique for each time zone,\nso that comparing the 32-bit values will tell you whether the\ntime zones are the same or not.\n<\/p>\n<p>\nFortunately, the customer also provided context for the\nquestion, explaining their underlying problem.\nAnd as is often the case, the customer had broken down the\nproblem into two parts, one easy and one impossible.\nThe customer solved the easy part\nand was asking for help with the impossible part.\n<\/p>\n<p>\nBut on closer inspection, the problem wasn&#8217;t so much impossible\nas it was improperly specified:\n<\/p>\n<blockquote CLASS=\"q\"><p>\nThe bigger problem I&#8217;m actually trying to solve is that we call\n<code>System&shy;Time&shy;To&shy;Tz&shy;Specific&shy;Local&shy;Time<\/code>\ninside a deeply\nnested loop.\nI would like to cache the results for performance,\nusing the time zone as a key to a <code>CAtl&shy;Map<\/code>\nwhich would hold the cached results for each time zone.\nI&#8217;m looking for help coming up with what combinaion of the\nstructure members to use to uniquely identify the time zone.\n<\/p><\/blockquote>\n<p>\nOkay,\nthe customer appears to be a bit confused about hash keys.\nHash keys do not need to be unique for each time zone.\nIt is perfectly legitimate for two different items to result\nin the same hash value;\nthat&#8217;s why we have the term <i>hash collision<\/i>.\nOf course, you want to take reasonable steps to minimize collisions,\nbut when you don&#8217;t control the domain space,\nhash collisions are a part of life.\n<\/p>\n<blockquote CLASS=\"q\">\n<p>\nFrom looking at some time zone data, it looks like\n<code>(Bias + Standard&shy;Bias)<\/code> is unique for any time zone,\nbut I know that there are a lot of complicated\nissues when dealing with time zones\nso I wanted to check if I could be sure of that.\n<\/p>\n<pre>\nLONG CTimeZoneTraits::GetHash(const TIME_ZONE_INFORMATION&amp; tz)\n{\n return tz.Bias + tz.StandardBias;\n}\nint CTimeZoneTraits::Equals(const TIME_ZONE_INFORMATION&amp; tz1,\n                            const TIME_ZONE_INFORMATION&amp; tz2)\n{\n return tz1.Bias         == tz2.Bias &amp;&amp;\n        tz1.StandardBias == tz2.StandardBias &amp;&amp;\n        tz1.DaylightBias == tz2.DaylightBias &amp;&amp;\n        memcmp(&amp;tz1.StandardDate, &amp;tz2.StandardDate,\n                                  sizeof(tz1.StandardDate) &amp;&amp;\n        memcmp(&amp;tz1.DaylightDate, &amp;tz2.DaylightDate,\n                                  sizeof(tz1.DaylightDate);\n}\n<\/pre>\n<\/blockquote>\n<p>\nIf you think it about it, it&#8217;s clear that\n<code>(Bias + Standard&shy;Bias)<\/code> does not always uniquely identify\na time zone.\nConsider two cities at the same longitude in the same hemisphere\nin the middle of winter:\nThey will have the same <code>Standard&shy;Bias<\/code>\n(because they have the same longitude) and the same\n<code>Bias<\/code>\n(because Daylight Saving Time is not applicable during the winter),\nbut if the cities are in different countries (or sometimes,\neven\n<a HREF=\"http:\/\/en.wikipedia.org\/wiki\/Time_in_Indiana\">\ndifferent parts of the same country<\/a>),\nthey will transition to\/from Daylight Saving Time differently\nand consequently do not belong to the same time zone.\n<\/p>\n<p>\nOn the other hand, since this is being used simply as a hash key,\nuniqueness is not an absolute requirement,\nso even a bad hash function will still &#8220;work&#8221;;\nit&#8217;ll just be slower than a good hash function.\n<\/p>\n<p>\nIf it were up to me, I would choose as a hash function\nsomething like this:\n<\/p>\n<pre>\nLONG CTimeZoneTraits::GetHash(const TIME_ZONE_INFORMATION&amp; tz)\n{\n return tz.StandardBias +\n        tz.StandardDate.wDay +\n        (tz.StandardDate.wDayOfWeek &lt;&lt; 16) +\n        (tz.StandardDate.wMonth &lt;&lt; 24);\n}\n<\/pre>\n<p>\nI wouldn&#8217;t use the <code>Bias<\/code> in the hash code because\nthe <code>Bias<\/code> changes over time.\nIf the hash table lifetime extends across a daylight saving time transition,\nthen the <code>Bias<\/code> will change.\n<\/p>\n<p>\nFor the hash, I use the <code>Standard&shy;Bias<\/code>, which is the number\nof minutes east of UTC.\nIn practice this does not exceed\n60&nbsp;&times;&nbsp;25&nbsp;=&nbsp;1500,\nand it&#8217;s a multiple of&nbsp;30.\n(But\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2007\/03\/09\/1840625.aspx\">\nnot necessarily a multiple of&nbsp;60<\/a>.)\nThe <code>wDay<\/code> is typically in the range [0,5], though it can\ngo as high as 31 if the transition is based on a specific day.\nTherefore, I&#8217;ll simply add it to the <code>Standard&shy;Bias<\/code>,\ntaking advantage of the fact that the <code>Standard&shy;Bias<\/code> is\na multiple of&nbsp;30.\nThe month and day of the week are thrown into the upper 16 bits.\n<\/p>\n<p>\nNow, this hash function will still have collisions: If there are\ntwo time zones at the same longitude\nwhich transition to Standard time with the same rule,\nbut which transition to Daylight time according to different rules,\nthen we will still have a collision.\n<\/p>\n<blockquote CLASS=\"q\"><p>\nI would like to reduce the number of collisions\nby understanding how often two equal values of\n<code>(Bias + Standard&shy;Bias)<\/code>\ncould represent different time zones.\n<\/p><\/blockquote>\n<p>\nHow likely is such a collision?\nYou can answer this question yourself:\nTake all the time zones currently known to the system and\nhash them all to see what happens.\nOf course,\n<a HREF=\"http:\/\/www.kyivpost.com\/news\/nation\/detail\/113166\/\">\ntime zones change all the time<\/a>,\nso don&#8217;t assume that\nyour results will hold true in perpetuity,\nbut if you&#8217;re just looking for a rough guide, calculating\nagainst the current state of affairs is a pretty good one.\nIt&#8217;s true that time zones change all the time,\nbut they typically don&#8217;t change by much.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Public Service Announcement: Daylight Saving Time ends 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 asked the following question: Given two TIME_ZONE_INFORMATION structures, I would like to compute a LONG for each that I can then compare [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25,108],"class_list":["post-9193","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code","tag-time"],"acf":[],"blog_post_summary":"<p>Public Service Announcement: Daylight Saving Time ends 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 asked the following question: Given two TIME_ZONE_INFORMATION structures, I would like to compute a LONG for each that I can then compare [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=9193"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9193\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=9193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}