C言語

タイムゾーンの一覧取得について

更新日:

タイムゾーン(time zone)とは

タイムゾーン(time zone)は、「地域の標準時を示すUTCとの時間差」でWindowsなどで設定する情報です。

プログラムで時刻を取得・変更するWindows APIはよく使われますが、この「タイムゾーンを取得・変更する」ということはめったにありません。

タイムゾーンを取得・変更するWindows API
タイムゾーンを取得・変更するWindows APIには

  • GetTimeZoneInformation関数(現在設定しているタイムゾーンを取得する)
  • SetTimeZoneInformation(タイムゾーンを設定する)

がありますが、実はこのAPIを呼ぶだけでは意味がありません。

それは、この2つのAPIは引数に同じ構造体を渡す関数なのですが、

  1. GetTimeZoneInformation関数で現在のタイムゾーンを取得する。
  2. 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;
}

 

実行した結果です。

実行画面1-1

Indexの値が異常です。
レジストリエディタでチェックしてみました。

Win10_Reg-1
レジストリに"Index"という値がありません。
「コードが間違えているのか?」と思い、たまたま残っていたWindows XPのPCのほうで同じレジストリを見てみました。

WinXP_Reg-1

"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;
}

 

下記が実行した結果です。

 

実行画面2-1

うまく、タイムゾーンの一覧を取得して、UTCからの時間差も取得されました。

 

-C言語

Copyright© プログラミングテクニック集キヤミー , 2019 All Rights Reserved.