タイムゾーン(time zone)とは
タイムゾーン(time zone)は、「地域の標準時を示すUTCとの時間差」でWindowsなどで設定する情報です。
プログラムで時刻を取得・変更するWindows APIはよく使われますが、この「タイムゾーンを取得・変更する」ということはめったにありません。
タイムゾーンを取得・変更するWindows API
タイムゾーンを取得・変更するWindows APIには
- GetTimeZoneInformation関数(現在設定しているタイムゾーンを取得する)
- SetTimeZoneInformation(タイムゾーンを設定する)
がありますが、実はこのAPIを呼ぶだけでは意味がありません。
それは、この2つのAPIは引数に同じ構造体を渡す関数なのですが、
- GetTimeZoneInformation関数で現在のタイムゾーンを取得する。
- SetTimeZoneInformation関数にGetTimeZoneInformationで取得したタイムゾーンを設定する。
とするだけでは、1.で取得したタイムゾーンは、現在設定されているタイムゾーンなのでタイムゾーンは変更されません。
タイムゾーンを変更するには、変更するための「タイムゾーンの一覧」がないといけないのです。
タイムゾーンの一覧はどこ?
タイムゾーンの一覧はレジストリにあります。
ただ、レジストリキーが
NT系Windows
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
非NT系Windows
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones
と2種類あるのですが、現行のWindows OSはほとんどがNT系Windowsになっているので
NT系Windows
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
にあります。
(NT系Windowsの”HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\”には”Time Zones”はないのでご注意を!!)
実際にタイムゾーンの一覧を取得してみる
下記のようなサンプルコードを書いて、Windows 10上で動かしてみました。
#include "stdafx.h" #include <Windows.h> #define TIME_REG _T("SOFTWARE\\Microsoft\\") \ _T("Windows NT\\CurrentVersion\\Time Zones") using namespace std; struct TIME_ZONE_INFO { ULONG Bias; ULONG StandardBias; ULONG DaylightBias; SYSTEMTIME StandardDate; SYSTEMTIME DaylightDate; }; void TimeZoneInfoToTimeZoneInformation( TIME_ZONE_INFORMATION& TimeZoneInfo1 , const TIME_ZONE_INFO& TimeZoneInfo2 ) { TimeZoneInfo1.Bias = TimeZoneInfo2.Bias ; TimeZoneInfo1.DaylightBias = TimeZoneInfo2.DaylightBias ; TimeZoneInfo1.DaylightDate = TimeZoneInfo2.DaylightDate ; TimeZoneInfo1.StandardBias = TimeZoneInfo2.StandardBias ; TimeZoneInfo1.StandardDate = TimeZoneInfo2.StandardDate ; } int _tmain(int argc, _TCHAR* argv[]) { HKEY hkTimeZones; int iErr = 1; bool bShow = false; INT dwIndexToFind = -1; for( int idx = 1 ; idx < argc && !bShow; idx++ ) { bShow = ( _tcsicmp( _T("-show") , argv[idx] ) == 0 ); } for( int idx = 1 ; idx < argc ; idx++ ) { if( _tcsicmp( _T("-index") , argv[idx] ) == 0 ) if( idx + 1 <= argc ) dwIndexToFind = _ttoi( argv[++idx] ); } if( ( bShow && dwIndexToFind == -1 ) || ( dwIndexToFind != -1 && !bShow ) ) { if( RegOpenKeyEx(HKEY_LOCAL_MACHINE,TIME_REG,0,KEY_READ,&hkTimeZones) == ERROR_SUCCESS ) { DWORD dwIndex = 0; TCHAR tcKeyName[512]; DWORD dwcbName = 512 * sizeof( TCHAR ); FILETIME ftLastWrite; while( RegEnumKeyEx(hkTimeZones,dwIndex++,tcKeyName, &dwcbName,NULL,NULL,NULL,&ftLastWrite) != ERROR_NO_MORE_ITEMS ) { HKEY hkTimeZone; if( RegOpenKeyEx(hkTimeZones,tcKeyName,0,KEY_READ,&hkTimeZone) == ERROR_SUCCESS ) { DWORD dwTimeZoneIndex; TIME_ZONE_INFO TZI; TIME_ZONE_INFORMATION TZI2; // Get Index DWORD dwDataSize = sizeof( DWORD ); RegQueryValueEx(hkTimeZone,_T("Index"),NULL, NULL,(BYTE*)&dwTimeZoneIndex,&dwDataSize); // Get TZI Upper Bytes dwDataSize = sizeof( TIME_ZONE_INFO ); RegQueryValueEx(hkTimeZone,_T("TZI"),NULL,NULL,(BYTE*)&TZI,&dwDataSize); TimeZoneInfoToTimeZoneInformation( TZI2 , TZI ); // Get Text Values dwDataSize = 32 * sizeof( TCHAR ); RegQueryValueEx(hkTimeZone,_T("Dlt"),NULL,NULL,(BYTE*)TZI2.DaylightName,&dwDataSize); dwDataSize = 32 * sizeof( TCHAR ); RegQueryValueEx(hkTimeZone,_T("Std"),NULL,NULL,(BYTE*)TZI2.StandardName,&dwDataSize); if( bShow ) { _tprintf( _T("Index: %d\n") , dwTimeZoneIndex ); _tprintf( _T(" STD: %s\n") , TZI2.StandardName ); _tprintf( _T(" DLT: %s\n") , TZI2.DaylightName ); } else { if( dwTimeZoneIndex == dwIndexToFind ) if( SetTimeZoneInformation( &TZI2 ) ) iErr = 0; } RegCloseKey( hkTimeZone ); } dwcbName = 512 * sizeof( TCHAR ); } RegCloseKey( hkTimeZones ); } } else { _tprintf(_T("\nUsage:\n\n")); _tprintf(_T("tz.exe -show\n")); _tprintf(_T("tz.exe -index [get index number") _T(" from -show command for your time zone]\n")); _tprintf(_T("\n\n\nLudvik Jerabek, 2005\n")); } return iErr; }
実行した結果です。
Indexの値が異常です。
レジストリエディタでチェックしてみました。
レジストリに”Index”という値がありません。
「コードが間違えているのか?」と思い、たまたま残っていたWindows XPのPCのほうで同じレジストリを見てみました。
“Index”という値がちゃんとあります。
Windows OSがバージョンアップした時に、”Index”というレジストリ値は使えなくなっていたようです。
UTCからの時差を取り扱っている’Bias’という情報は、単位は’分’なので今回は’時(hour)’で表示するようにコードを下記のように変更しました。
#include "stdafx.h" #include <Windows.h> #define _OLD_WINDOWS (0) // ターゲットのOSが昔のWindowsの場合は1 #if(_OLD_WINDOWS == 0) #define TIME_REG _T("SOFTWARE\\Microsoft\\") \ _T("Windows NT\\CurrentVersion\\Time Zones") #else #define TIME_REG _T("SOFTWARE\\Microsoft\\") \ _T("Windows\\CurrentVersion\\Time Zones") #endif // #if(_OLD_WINDOWS == 0) using namespace std; struct TIME_ZONE_INFO { ULONG Bias; ULONG StandardBias; ULONG DaylightBias; SYSTEMTIME StandardDate; SYSTEMTIME DaylightDate; }; void TimeZoneInfoToTimeZoneInformation( TIME_ZONE_INFORMATION& TimeZoneInfo1 , const TIME_ZONE_INFO& TimeZoneInfo2 ) { TimeZoneInfo1.Bias = TimeZoneInfo2.Bias ; TimeZoneInfo1.DaylightBias = TimeZoneInfo2.DaylightBias ; TimeZoneInfo1.DaylightDate = TimeZoneInfo2.DaylightDate ; TimeZoneInfo1.StandardBias = TimeZoneInfo2.StandardBias ; TimeZoneInfo1.StandardDate = TimeZoneInfo2.StandardDate ; } int _tmain(int argc, _TCHAR* argv[]) { HKEY hkTimeZones; int iErr = 1; bool bShow = false; INT dwIndexToFind = -1; for( int idx = 1 ; idx < argc && !bShow; idx++ ) { bShow = ( _tcsicmp( _T("-show") , argv[idx] ) == 0 ); } for( int idx = 1 ; idx < argc ; idx++ ) { if( _tcsicmp( _T("-index") , argv[idx] ) == 0 ) if( idx + 1 <= argc ) dwIndexToFind = _ttoi( argv[++idx] ); } if( ( bShow && dwIndexToFind == -1 ) || ( dwIndexToFind != -1 && !bShow ) ) { if( RegOpenKeyEx(HKEY_LOCAL_MACHINE,TIME_REG,0,KEY_READ,&hkTimeZones) == ERROR_SUCCESS ) { DWORD dwIndex = 0; TCHAR tcKeyName[512]; DWORD dwcbName = 512 * sizeof( TCHAR ); FILETIME ftLastWrite; while( RegEnumKeyEx(hkTimeZones,dwIndex++,tcKeyName, &dwcbName,NULL,NULL,NULL,&ftLastWrite) != ERROR_NO_MORE_ITEMS ) { HKEY hkTimeZone; if( RegOpenKeyEx(hkTimeZones,tcKeyName,0,KEY_READ,&hkTimeZone) == ERROR_SUCCESS ) { DWORD dwTimeZoneIndex; TIME_ZONE_INFO TZI; TIME_ZONE_INFORMATION TZI2; // Get Index DWORD dwDataSize = sizeof( DWORD ); #if(_OLD_WINDOWS == 0) RegQueryValueEx(hkTimeZone,_T("Index"),NULL, NULL,(BYTE*)&dwTimeZoneIndex,&dwDataSize); #endif // #ifdef (_OLD_WINDOWS == 0) // Get TZI Upper Bytes dwDataSize = sizeof( TIME_ZONE_INFO ); RegQueryValueEx(hkTimeZone,_T("TZI"),NULL,NULL,(BYTE*)&TZI,&dwDataSize); TimeZoneInfoToTimeZoneInformation( TZI2 , TZI ); // Get Text Values dwDataSize = 32 * sizeof( TCHAR ); RegQueryValueEx(hkTimeZone,_T("Dlt"),NULL,NULL,(BYTE*)TZI2.DaylightName,&dwDataSize); dwDataSize = 32 * sizeof( TCHAR ); RegQueryValueEx(hkTimeZone,_T("Std"),NULL,NULL,(BYTE*)TZI2.StandardName,&dwDataSize); if( bShow ) { #if(_OLD_WINDOWS == 1) _tprintf( _T("Index: %d\n") , dwTimeZoneIndex ); #endif // #ifdef (_OLD_WINDOWS == 1) _tprintf( _T(" STD: %s\n") , TZI2.StandardName ); _tprintf( _T(" DLT: %s\n") , TZI2.DaylightName ); // 追加したコード --> _tprintf( _T(" Bias: %6.2lf\n\n") , (TZI2.Bias / 60.0) ); // Biasは分(minute)単位なので時(hour)単位にする // --> 追加したコード } else { if( dwTimeZoneIndex == dwIndexToFind ) if( SetTimeZoneInformation( &TZI2 ) ) iErr = 0; } RegCloseKey( hkTimeZone ); } dwcbName = 512 * sizeof( TCHAR ); } RegCloseKey( hkTimeZones ); } } else { _tprintf(_T("\nUsage:\n\n")); _tprintf(_T("tz.exe -show\n")); _tprintf(_T("tz.exe -index [get index number") _T(" from -show command for your time zone]\n")); _tprintf(_T("\n\n\nLudvik Jerabek, 2005\n")); } return iErr; }
下記が実行した結果です。
うまく、タイムゾーンの一覧を取得して、UTCからの時間差も取得されました。