Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WinCE: Let Your App Understand UTC

0.00/5 (No votes)
4 Jul 2012 1  
A workaround to convert UTC to the LocalTime, in summer but without DST flag

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 Smile | <img src= " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />

Above all Smile | <img src= " 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:
  // Check of the time law before the app session
  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) {
      // Trick's kerenel (Afganistan time law): 
      ::ZeroMemory(&tzinfo.StandardDate, sizeof(SYSTEMTIME));
      ::ZeroMemory(&tzinfo.DaylightDate, sizeof(SYSTEMTIME));
      SetTimeZoneInformation(&tzinfo);
    }
  }

  // Registry dump of the original time zone information
  typedef struct {
    LONG Bias;
    LONG StandardBias;
    LONG DaylightBias;
    SYSTEMTIME StandardDate;
    SYSTEMTIME DaylightDate;
  } SRegTZI;

  // Find an original time zone information per its localized display name
  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;
  }

  // Repair of the original time law
  void ResetMicrosoftClock()
  {
    TIME_ZONE_INFORMATION tzinfo = {0};
    GetTimeZoneInformation(&tzinfo);
    if (tzinfo.StandardName) {
      SRegTZI sRegTZI = {0};
      if (GetTimeZoneRegTZI(tzinfo.StandardName, sRegTZI)) {
        if (sRegTZI.DaylightDate.wMonth) {
          // Repair of the original Information
          ::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 Smile | <img src= " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />

History 

UTC/GMT is 08:27, 4. Juli 2012  - Created.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here