{"id":5,"date":"2013-04-10T10:57:38","date_gmt":"2013-04-10T10:57:38","guid":{"rendered":"http:\/\/blog.rophuine.net\/coder\/?p=5"},"modified":"2018-01-12T11:57:54","modified_gmt":"2018-01-12T01:57:54","slug":"creating-custom-windows-time-zones","status":"publish","type":"post","link":"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/","title":{"rendered":"Creating Custom Windows Time Zones"},"content":{"rendered":"<p>I spend a lot of time dealing with time zones. I generally use one of two approaches:<\/p>\n<ol>\n<li>Store an offset<\/li>\n<li>Store a Windows Time Zone identifier<\/li>\n<\/ol>\n<p>Method one is quick and straight-forward, but not very flexible: it doesn&#8217;t account for daylight savings, for a start. Method two is more complex, but generally more powerful &#8211; .Net gives you some great tools for working with time zones, and has some basic safety checks which (sometimes) prevent you from doing silly things like doubling up your time zone conversions.<\/p>\n<div>\n<p>\u00a0I recently dealt with an interesting issue: a non-standard time zone.<\/p>\n<\/div>\n<div>\n<p>\u00a0We retrieve data from lots of remote monitoring equipment. We talk to a variety of different types of gear, most of which are designed to be simple and reliable, and operate using as little power as possible. In practice, this means the equipment uses a simple clock, with no time zone awareness. Industry practice (which I&#8217;m often stuck with) is to configure the equipment with the local time (which violates rule 1: Do everything in UTC.)<\/p>\n<\/div>\n<div>\n<p>\u00a0This particular piece of equipment had been deployed during the summer, while daylight saving was in effect, and the clock had been set to the local daylight time: UTC+10.5 (yes, there are time zones on the half hour!). When we configured the data connector at the server end, we spotted a handy time zone with the right offset (Adelaide) and set it to use that. Of course, nobody thought about daylight saving, and so when it ended our Adelaide time zone suddenly reverted to UTC+9.5, and so all of the incoming data started being treated as if it were UTC+9.5 instead of UTC+10.5 &#8211; which was a problem.<\/p>\n<\/div>\n<div>\n<p>\u00a0Of course, we&#8217;ve dealt with this sort of problem before: most of our equipment has no daylight saving support, but lots of our customers are in places which follow daylight saving. We normally just find a time zone with the correct offset but no DST: for example, gear in Sydney gets set to either +10 (and we use the Brisbane time zone) or +11 (and we use somewhere like Port Vila, which is +11 year-round). However, when we went to find somewhere which was on UTC+10.5 year round, we ran into a problem. There is no such place.<\/p>\n<\/div>\n<div>\n<p>\u00a0We didn&#8217;t really want to make code changes to support this situation, but we had to do something, and it didn&#8217;t take much digging to discover that .Net pulls its time zone information from the Windows registry. After a quick look at the relevant keys, however, it quickly became obvious that it wasn&#8217;t going to be simple to craft a custom entry. The Adelaide key looks like this:<\/p>\n<\/div>\n<p><a href=\"http:\/\/www.nerdhold.com\/coder\/files\/2013\/04\/Registry-Editor-Timezone.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-10\" alt=\"Regedit showing cryptic binary field for time zone configuration\" src=\"http:\/\/www.nerdhold.com\/coder\/files\/2013\/04\/Registry-Editor-Timezone.png\" width=\"535\" height=\"182\" srcset=\"http:\/\/www.nerdhold.com\/coder\/files\/2013\/04\/Registry-Editor-Timezone.png 535w, http:\/\/www.nerdhold.com\/coder\/files\/2013\/04\/Registry-Editor-Timezone-300x102.png 300w, http:\/\/www.nerdhold.com\/coder\/files\/2013\/04\/Registry-Editor-Timezone-500x170.png 500w\" sizes=\"(max-width: 535px) 100vw, 535px\" \/><\/a><\/p>\n<div>\n<p>The first six entries are fine. The three values with names starting with &#8216;MUI&#8217; are just localisation references, and because we&#8217;re just doing this on one of our servers, we don&#8217;t care about that &#8211; and it turns out that you can ignore the whole @dll syntax and just put a string in here. Great!<\/p>\n<\/div>\n<div>\n<p>\u00a0That TZI value looks nasty &#8211; and it turns out that it is. Fortunately, Microsoft provides\u00a0<a href=\"http:\/\/support.microsoft.com\/kb\/914387\" data-blogger-escaped-target=\"_blank\">an editor<\/a>\u00a0to modify these entries. Unfortunately, it was buried so thoroughly that we didn&#8217;t find it at the time (nor did\u00a0<a href=\"http:\/\/stackoverflow.com\/questions\/598695\/custom-time-zone\" data-blogger-escaped-target=\"_blank\">StackOverflow<\/a>!) and so we pushed on with our research. We found our answers in the\u00a0<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms725481(v=vs.85).aspx\" data-blogger-escaped-target=\"_blank\">MSDN TIME_ZONE_INFORMATION structure page<\/a>. Despite the fact that the whole point of the registry is to provide convenient key\/value pairs, Microsoft decided to go with storing a C struct in hex values. The TZI field is a hex dump of the _REG_TZI_FORMAT struct:<\/p>\n<\/div>\n<div>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\ntypedef struct _REG_TZI_FORMAT\r\n{\r\n    LONG Bias;\r\n    LONG StandardBias;\r\n    LONG DaylightBias;\r\n    SYSTEMTIME StandardDate;\r\n    SYSTEMTIME DaylightDate;\r\n} REG_TZI_FORMAT;\r\n<\/pre>\n<p>Two things immediately stood out.\u00a0<em>c6 fd ff ff<\/em>\u00a0was not +9.5, nor was it +570 (if it were stored in minutes). It turns out the registry stores the negative of the time zone offset in minutes &#8211; if you fire up your programmer&#8217;s calculator, you&#8217;ll see that 0xfffffdc6 is, in fact, -570. This isn&#8217;t the only quirk &#8211; the date fields have some pretty specific requirements, the DaylightBias is in fact the\u00a0<em>difference<\/em>\u00a0between the daylight total offset and the Bias field, and the StandardBias is an optional offset from the Bias field to the standard (non-DST) time, which is generally (always?) set to 0.Because I don&#8217;t like to half-do things (and I spotted a chance to brush off my rather dusty C++ skills) I built a tool to accept the required fields and churn out a file ready to be imported directly into the registry (keep in mind at this stage we hadn&#8217;t found TZEdit). It&#8217;s unlikely to ever be polished, but it&#8217;s\u00a0<a href=\"https:\/\/github.com\/Rophuine\/TimeZoneInfoGenerator\" data-blogger-escaped-target=\"_blank\">available on GitHub<\/a>.<\/p>\n<p>I don&#8217;t really know what to make of this experience. I could have saved myself some work by continuing to search for a ready-made time zone editor, but the clock was ticking to get this fixed, and I found enough information to solve the immediate problem in much less time than it took me to find the editor (I didn&#8217;t build the registry file generator until after we had hand-crafted the registry entries we needed).<\/p>\n<p>I think the real lesson to take out of this is that storing binary data like the TZI field destroys the usability of the registry. If Microsoft had used a few sensibly-named fields instead of dumping cryptically-formatted binary data into a single field, we could have solved the problem with much less effort.<\/p>\n<p>Keep this in mind when building your own configuration systems!<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>I spend a lot of time dealing with time zones. I generally use one of two approaches: Store an offset Store a Windows Time Zone identifier Method one is quick and straight-forward, but not very flexible: it doesn&#8217;t account for daylight savings, for a start. Method two is more complex, but generally more powerful &#8211; &hellip; <a href=\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Creating Custom Windows Time Zones<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[4,3],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.8 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Creating Custom Windows Time Zones - Nerdhold Coder<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Creating Custom Windows Time Zones - Nerdhold Coder\" \/>\n<meta property=\"og:description\" content=\"I spend a lot of time dealing with time zones. I generally use one of two approaches: Store an offset Store a Windows Time Zone identifier Method one is quick and straight-forward, but not very flexible: it doesn&#8217;t account for daylight savings, for a start. Method two is more complex, but generally more powerful &#8211; &hellip; Continue reading Creating Custom Windows Time Zones &rarr;\" \/>\n<meta property=\"og:url\" content=\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/\" \/>\n<meta property=\"og:site_name\" content=\"Nerdhold Coder\" \/>\n<meta property=\"article:published_time\" content=\"2013-04-10T10:57:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-01-12T01:57:54+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/www.nerdhold.com\/coder\/files\/2013\/04\/Registry-Editor-Timezone.png\" \/>\n<meta name=\"author\" content=\"Lionell Pack\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lionell Pack\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/\",\"url\":\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/\",\"name\":\"Creating Custom Windows Time Zones - Nerdhold Coder\",\"isPartOf\":{\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#website\"},\"datePublished\":\"2013-04-10T10:57:38+00:00\",\"dateModified\":\"2018-01-12T01:57:54+00:00\",\"author\":{\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77\"},\"breadcrumb\":{\"@id\":\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"http:\/\/www.nerdhold.com\/coder\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Creating Custom Windows Time Zones\"}]},{\"@type\":\"WebSite\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#website\",\"url\":\"http:\/\/www.nerdhold.com\/coder\/\",\"name\":\"Nerdhold Coder\",\"description\":\"Tinkerings of a C# Coder\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"http:\/\/www.nerdhold.com\/coder\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77\",\"name\":\"Lionell Pack\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/image\/\",\"url\":\"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg\",\"contentUrl\":\"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg\",\"caption\":\"Lionell Pack\"},\"sameAs\":[\"http:\/\/blog.rophuine.net\"],\"url\":\"http:\/\/www.nerdhold.com\/coder\/author\/admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Creating Custom Windows Time Zones - Nerdhold Coder","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/","og_locale":"en_US","og_type":"article","og_title":"Creating Custom Windows Time Zones - Nerdhold Coder","og_description":"I spend a lot of time dealing with time zones. I generally use one of two approaches: Store an offset Store a Windows Time Zone identifier Method one is quick and straight-forward, but not very flexible: it doesn&#8217;t account for daylight savings, for a start. Method two is more complex, but generally more powerful &#8211; &hellip; Continue reading Creating Custom Windows Time Zones &rarr;","og_url":"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/","og_site_name":"Nerdhold Coder","article_published_time":"2013-04-10T10:57:38+00:00","article_modified_time":"2018-01-12T01:57:54+00:00","og_image":[{"url":"http:\/\/www.nerdhold.com\/coder\/files\/2013\/04\/Registry-Editor-Timezone.png"}],"author":"Lionell Pack","twitter_misc":{"Written by":"Lionell Pack","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/","url":"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/","name":"Creating Custom Windows Time Zones - Nerdhold Coder","isPartOf":{"@id":"http:\/\/www.nerdhold.com\/coder\/#website"},"datePublished":"2013-04-10T10:57:38+00:00","dateModified":"2018-01-12T01:57:54+00:00","author":{"@id":"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77"},"breadcrumb":{"@id":"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/"]}]},{"@type":"BreadcrumbList","@id":"http:\/\/www.nerdhold.com\/coder\/2013\/04\/10\/creating-custom-windows-time-zones\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http:\/\/www.nerdhold.com\/coder\/"},{"@type":"ListItem","position":2,"name":"Creating Custom Windows Time Zones"}]},{"@type":"WebSite","@id":"http:\/\/www.nerdhold.com\/coder\/#website","url":"http:\/\/www.nerdhold.com\/coder\/","name":"Nerdhold Coder","description":"Tinkerings of a C# Coder","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"http:\/\/www.nerdhold.com\/coder\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/ca2988d5c0cb756a846e4d8c54e86b77","name":"Lionell Pack","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"http:\/\/www.nerdhold.com\/coder\/#\/schema\/person\/image\/","url":"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg","contentUrl":"http:\/\/0.gravatar.com\/avatar\/9be7b23cd97814ac4a40b9b4d2955b5a?s=96&d=mm&r=pg","caption":"Lionell Pack"},"sameAs":["http:\/\/blog.rophuine.net"],"url":"http:\/\/www.nerdhold.com\/coder\/author\/admin\/"}]}},"_links":{"self":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts\/5"}],"collection":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/comments?post=5"}],"version-history":[{"count":11,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts\/5\/revisions"}],"predecessor-version":[{"id":128,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/posts\/5\/revisions\/128"}],"wp:attachment":[{"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/media?parent=5"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/categories?post=5"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.nerdhold.com\/coder\/wp-json\/wp\/v2\/tags?post=5"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}