{"id":105255,"date":"2021-05-27T07:00:00","date_gmt":"2021-05-27T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105255"},"modified":"2021-05-28T10:06:54","modified_gmt":"2021-05-28T17:06:54","slug":"20210527-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210527-00\/?p=105255","title":{"rendered":"How can I convert between IANA time zones and Windows registry-based time zones?"},"content":{"rendered":"<p>A customer wanted to be able to convert from IANA time zones and Windows registry-based time zones. Is there a function for doing that?<\/p>\n<p>Yes, but it&#8217;s not somewhere you might think to look.<\/p>\n<p>The Windows globalization team emphasizes that what you should <i>not<\/i> do is take the information from <a href=\"http:\/\/cldr.unicode.org\/\"> CLDR<\/a>, use it to produce a translation table, and hard-code that table into your application. Time zones are notoriously fickle, and there&#8217;s <a href=\"https:\/\/www.wired.com\/story\/microsoft-time-lords\/\"> a team of people inside Microsoft whose full-time job is to keep track of time zone changes worldwide<\/a>.<\/p>\n<p>What you need to do is delegate the work of updating time zone information to somebody else.<\/p>\n<p>One such <i>somebody else<\/i> is ICU, the <a href=\"http:\/\/site.icu-project.org\/\"> International Components for Unicode<\/a>.<\/p>\n<p>A copy of ICU has been included with Windows since Windows 10 Version 1703 (build 15063). All you have to do is include <code>icu.h<\/code>, and you&#8217;re off to the races. An advantage of using the version that comes with Windows is that it is actively maintained and updated by the Windows team. If you need to run on older systems, you can build your own copy from <a href=\"https:\/\/github.com\/microsoft\/icu\"> their fork of the ICU repo<\/a>, but the job of servicing the project is now on you.<\/p>\n<p>Here&#8217;s a proof-of-concept program that shows how to convert between IANA time zones and Windows time zones.<\/p>\n<pre>#include &lt;windows.h&gt;\r\n#define UCHAR_TYPE wchar_t\r\n#include &lt;icu.h&gt;\r\n\r\nint wmain(int argc, wchar_t** argv)\r\n{\r\n    wchar_t buffer[128];\r\n    UErrorCode status = U_ZERO_ERROR;\r\n    if (argc == 2) {\r\n        auto result = ucal_getWindowsTimeZoneID(\r\n            argv[1], -1, buffer, ARRAYSIZE(buffer), &amp;status);\r\n        if (U_SUCCESS(status)) {\r\n            printf(\"result = %d, IANA %ls -&gt; Windows %ls\\n\",\r\n                   result, argv[1], buffer);\r\n        }\r\n    } else if (argc == 3) {\r\n        char region[64];\r\n        if (WideCharToMultiByte(CP_UTF8, 0, argv[2], -1,\r\n                                region, 64, 0, nullptr)) {\r\n            auto result = ucal_getTimeZoneIDForWindowsID(\r\n                argv[1], -1, region, buffer, ARRAYSIZE(buffer), &amp;status);\r\n            if (U_SUCCESS(status)) {\r\n                printf(\"result = %d, Windows %ls:%s -&gt; IANA %ls\\n\",\r\n                       result, argv[1], region, buffer);\r\n            }\r\n        }\r\n    }\r\n    return 0;\r\n}\r\n<\/pre>\n<p>By default, the <code>icu.h<\/code> header uses <code>char16_t<\/code> to represent UTF-16 code units. Windows, however, uses <code>wchar_t<\/code>, so we define <code>UCHAR_TYPE<\/code> to get the type we want.<\/p>\n<p>If you run the program with a single command line parameter, then we treat it as an IANA time zone and ask <code>ucal_<wbr \/>get\u00adWindows\u00adTime\u00adZone\u00adID<\/code> to convert it to a Windows time zone.<\/p>\n<pre>C:\\&gt; scratch America\/Vancouver\r\nresult = 21, IANA America\/Vancouver -&gt; Windows Pacific Standard Time\r\n<\/pre>\n<p>If you run it with two command line parameters, then we treat the first as the Windows time zone and the second as the region, and convert the pair to an IANA time zone.<\/p>\n<pre>C:\\&gt; scratch \"Pacific Standard Time\" US\r\nresult = 19, Windows Pacific Standard Time:US -&gt; IANA America\/Los_Angeles\r\n\r\nC:\\&gt; scratch \"Pacific Standard Time\" CA\r\nresult = 17, Windows Pacific Standard Time:CA -&gt; IANA America\/Vancouver\r\n<\/pre>\n<p>To resolve these calls to the system-provided <code>ICU.DLL<\/code>, link either with <code>icu.lib<\/code> or with the umbrella library <code>windowsapp.lib<\/code>.<\/p>\n<p><b>Bonus chatter<\/b>: The Windows globalization team also strongly recommends that programs use IANA time zones and use the Windows registry-based time zones only for legacy interop purposes.<\/p>\n<p><b>Important bonus chatter<\/b>: Even though support arrived in Windows 10 Version 1703 (build 15063), using ICU prior to Windows 10 Version 1903 (build 18362) is a little trickier. When it was introduced, the ICU library was added as two system DLLs:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Feature<\/th>\n<th>Header file<\/th>\n<th>Library<\/th>\n<th>DLL<\/th>\n<\/tr>\n<tr>\n<td>Common<\/td>\n<td><code>icucommon.h<\/code><\/td>\n<td><code>icuuc.lib<\/code><\/td>\n<td><code>icuuc.dll<\/code><\/td>\n<\/tr>\n<tr>\n<td>Internationalization<\/td>\n<td><code>icui18n.h<\/code><\/td>\n<td><code>icuin.lib<\/code><\/td>\n<td><code>icuin.dll<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In Windows 10 Version 1709, the two header files were combined into a single <code>icu.h<\/code> header file.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Feature<\/th>\n<th>Header file<\/th>\n<th>Library<\/th>\n<th>DLL<\/th>\n<\/tr>\n<tr>\n<td>Common<\/td>\n<td rowspan=\"2\"><code>icu.h<\/code><\/td>\n<td><code>icuuc.lib<\/code><\/td>\n<td><code>icuuc.dll<\/code><\/td>\n<\/tr>\n<tr>\n<td>Internationalization<\/td>\n<td><code>icuin.lib<\/code><\/td>\n<td><code>icuin.dll<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The separate header files still remain for source code backward compatibility, but no new features will be added to the separate header files.<\/p>\n<p>And in Windows 10 Version 1903, the separate LIBs and DLLs were also combined:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Feature<\/th>\n<th>Header file<\/th>\n<th>Library<\/th>\n<th>DLL<\/th>\n<\/tr>\n<tr>\n<td>Common<\/td>\n<td rowspan=\"2\"><code>icu.h<\/code><\/td>\n<td rowspan=\"2\"><code>icu.lib<\/code><\/td>\n<td rowspan=\"2\"><code>icu.dll<\/code><\/td>\n<\/tr>\n<tr>\n<td>Internationalization<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Again, the old LIBs and DLLs remain for backward compatibility. (The old DLLs are now forwarders to the combined <code>icu.dll<\/code>.)<\/p>\n<p>This change was triggered by a breaking change in the underlying ICU library that moved one of the functions from Common to Internationalization. Since the ICU team had to do some architectural work to accommodate the breaking change, they took the time to do some other tidying.<\/p>\n<p>Combining two DLLs into one DLL allowed for a reduction in the overall footprint of ICU. They also removed the requirement that you call <code>Co\u00adInitialize\u00adEx<\/code> before calling any ICU functions. (In earlier versions, if you failed to call <code>Co\u00adInitialize\u00adEx<\/code>, then the default ICU locale is always <tt>en_US<\/tt> and the default time zone is always <tt>Etc\/UTC<\/tt>.)<\/p>\n<p>TL;DR: If you want to support versions prior to Windows 10 Version 1903, you should load the appropriate separately-named DLL and make sure to call <code>Co\u00adInitialize\u00adEx<\/code>.<\/p>\n<p>To be sure you&#8217;re getting the one that doesn&#8217;t require COM to be initialized, go straight to <code>icu.dll<\/code>. (That&#8217;s <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/1d9e50cb4735df46d3de0cee5791e97295eaf588\/src\/libraries\/Native\/Unix\/System.Globalization.Native\/pal_icushim.c#L96\"> what .NET Core does<\/a>.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>ICU to the rescue.<\/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],"class_list":["post-105255","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>ICU to the rescue.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105255","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=105255"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105255\/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=105255"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105255"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}