Windowsのローカルタイムゾーン設定をtz database名で取得しようとすると、結構めんどくさい。
MSDNで検索すると、タイムゾーンを取得するには、GetTimeZoneInformation
を使えとある。
# include <windows.h>
int main()
{
TIME_ZONE_INFORMATION tzi;
memset(&tzi, 0,sizeof(tzi));
GetTimeZoneInformation(&tzi);
// #define UNICODEしていなくてもwchar_tで取得される
MessageBoxW(NULL, tzi.StandardName, NULL, MB_OK);
return 0;
}
これで取得できる名称は「東京 (標準時)」のような文字列で、おそらく中国語のWindowsなら「东京」とかが取得できちゃう、これ誰が得するんだよという糞仕様のAPIなので、これをまず英語名に変換するため、レジストリを検索する。
レジストリの"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"に、英語名のディレクトリ名が並んでいて、その中のStdに、タイムゾーンの日本語名が入っているので、これを使う。
# include <windows.h>
# include <stdio.h>
enum {
MAX_NAME_LEN = 64,
};
static void convert_english_tzname(char *dst, const wchar_t *src)
{
const wchar_t *path = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones";
HKEY hKey;
int i;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
strcpy(dst, "UTC");
return;
}
for (i = 0; ; i++) {
wchar_t wbuf[MAX_NAME_LEN];
DWORD wbuf_size = MAX_NAME_LEN;
switch (RegEnumKeyExW(hKey, i, wbuf, &wbuf_size, 0, NULL, NULL, NULL)) {
case ERROR_SUCCESS: {
HKEY hKey2;
if (RegOpenKeyExW(hKey, wbuf, 0, KEY_READ, &hKey2) == ERROR_SUCCESS) {
wchar_t wbuf2[MAX_NAME_LEN];
DWORD wbuf2_size = MAX_NAME_LEN * sizeof(wchar_t);
DWORD type;
if (RegQueryValueExW(hKey2, L"Std", NULL, &type, (BYTE*)wbuf2, &wbuf2_size) == ERROR_SUCCESS) {
if (type == REG_SZ && wcscmp(wbuf2, src) == 0) {
WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, dst, 64, NULL, NULL);
RegCloseKey(hKey2);
goto BREAK;
}
}
RegCloseKey(hKey2);
}
break;
}
case ERROR_NO_MORE_ITEMS:
strcpy(dst, "UTC");
goto BREAK;
default:
break;
}
}
BREAK:
RegCloseKey(hKey);
}
int main()
{
char cbuf[MAX_NAME_LEN];
TIME_ZONE_INFORMATION tzi;
memset(&tzi, 0,sizeof(tzi));
GetTimeZoneInformation(&tzi);
convert_english_tzname(cbuf, tzi.StandardName);
printf("%s\n", cbuf);
return 0;
}
これでやっとタイムゾーンの英語名が取得できたが、もちろんこれで終わりではない。
Windowsのタイムゾーン名とtz databaseのタイムゾーン名の変換は、以下の表を使うとよいが、一対一の対応になっていないことに注意。
新たに国が独立してタイムゾーンが増えたりしない限り、この表を更新する必要はない。