Introduction
Hello. Here is a trick to fix a Windows CE bug in the time convertation from its UTC (Coordinated Universal Time) to the Local (shown by the taskbar clock) form, in the context of the time zones which are providing DST (Daylight Saving Time) in theory, but that is not wanted currently at the user level:
Background
With this trick, any saved time stamp in UTC will be correctly translated into the local time, for example by the call of CTime::GetLocalTm(..)
or just of ::_localtime64_s(..)
, and in the described above context as well.
Using the code
The trick has been tested at the Windows CE versions 5 and 6.
The CRT library does initialize the "auto-DST" global variable by TRUE, and there is the only one code context that changes it to FALSE: the Time Zone Information should have NULL-dates for the standard (winter) and daylight (summer) times. It is a case for some Time Zones wich are not providing any time switching (for example Afganistan). It is also the case for other "switchable" Zones in winter or - when it is not wanted by User - on PC Windows, but not on CE Windows " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />
Above all " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> , the CRT library makes this check once only, at the first necessity of a time convertation .
That is why I have implemented the trick as a global class instance and placed it before the app instance:
class CE_MicrosoftClock
{
public:
CE_MicrosoftClock() { ResetMicrosoftClock(); SetMicrosoftClock(); }
~CE_MicrosoftClock() { ResetMicrosoftClock(); }
private:
void SetMicrosoftClock()
{
TIME_ZONE_INFORMATION tzinfo = {0};
GetTimeZoneInformation(&tzinfo);
bool bAutoDST(true);
HKEY hRegKey;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
_T("SOFTWARE\\Microsoft\\Clock"),
0, KEY_READ, &hRegKey)) {
DWORD dwType, dwValue, dwSize = sizeof(DWORD);
if (ERROR_SUCCESS == RegQueryValueEx(hRegKey,
_T("AutoDST"),
NULL, &dwType,
(BYTE*) &dwValue,
&dwSize)) {
bAutoDST = (0 != dwValue);
}
RegCloseKey(hRegKey);
}
if (!bAutoDST &&
tzinfo.DaylightDate.wMonth) {
::ZeroMemory(&tzinfo.StandardDate, sizeof(SYSTEMTIME));
::ZeroMemory(&tzinfo.DaylightDate, sizeof(SYSTEMTIME));
SetTimeZoneInformation(&tzinfo);
}
}
typedef struct {
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} SRegTZI;
bool GetTimeZoneRegTZI(LPCTSTR lpszLocalStdName, SRegTZI& sRegTZI)
{
bool bResult(false);
::ZeroMemory(&sRegTZI, sizeof(SRegTZI));
HKEY hRegKey;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
_T("Time Zones"),
0,
KEY_READ,
&hRegKey)) {
enum { eMaxNameLen = 255 };
DWORD dwNameLen = eMaxNameLen;
TCHAR szSubKeyName[eMaxNameLen +1] = {0};
DWORD dwSubKeyIdx(0);
bool bIteration(true);
while (bIteration &&
ERROR_SUCCESS != RegEnumKeyEx(hRegKey,
dwSubKeyIdx,
szSubKeyName,
&dwNameLen,
NULL,
NULL,
NULL,
NULL)) {
if (dwNameLen && szSubKeyName) {
HKEY hRegSubKey;
CString cszSubKeyName;
cszSubKeyName.Format(_T("Time Zones\\%s"), szSubKeyName);
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
cszSubKeyName,
0,
KEY_READ,
&hRegSubKey)) {
szSubKeyName[0] = _TCHAR(0);
dwNameLen = eMaxNameLen;
DWORD dwType(0);
if (ERROR_SUCCESS == RegQueryValueEx(hRegSubKey,
_T("Std"),
NULL,
&dwType,
(BYTE*) szSubKeyName,
&dwNameLen)) {
if (dwNameLen && szSubKeyName) {
CString cszFind(lpszLocalStdName);
cszSubKeyName = szSubKeyName;
if (0 == cszFind.Compare(cszSubKeyName)) {
bIteration = false;
dwNameLen = sizeof(SRegTZI);
if (ERROR_SUCCESS == RegQueryValueEx(hRegSubKey,
_T("TZI"),
NULL,
&dwType,
(BYTE*) &sRegTZI,
&dwNameLen)) {
bResult = true;
}
}
}
}
RegCloseKey(hRegSubKey);
}
}
dwSubKeyIdx++;
szSubKeyName[0] = _TCHAR(0);
dwNameLen = eMaxNameLen;
}
RegCloseKey(hRegKey);
}
return bResult;
}
void ResetMicrosoftClock()
{
TIME_ZONE_INFORMATION tzinfo = {0};
GetTimeZoneInformation(&tzinfo);
if (tzinfo.StandardName) {
SRegTZI sRegTZI = {0};
if (GetTimeZoneRegTZI(tzinfo.StandardName, sRegTZI)) {
if (sRegTZI.DaylightDate.wMonth) {
::memcpy_s(&tzinfo.StandardDate,
sizeof(SYSTEMTIME),
&sRegTZI.StandardDate,
sizeof(SYSTEMTIME));
::memcpy_s(&tzinfo.DaylightDate,
sizeof(SYSTEMTIME),
&sRegTZI.DaylightDate,
sizeof(SYSTEMTIME));
SetTimeZoneInformation(&tzinfo);
}
}
}
}
} global_clock_check;
CCETimerApp theApp;
Points of Interest
As you see the trick works in the startup phase only and will not react to any later modification of the date/time properties in the system. So the application must be restarted after a Time Zone Info manipulation. That is a usage "limitation" of the trick " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />
History
UTC/GMT is
08:27, 4. Juli 2012
- Created.