タイムゾーン(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からの時間差も取得されました。

