Windows Mobile: SetSystemTime and DST, Einstein’s Relativity Theory?

Wow, as I first saw this issue, I thought I was facing Einstein’s Relativity Theory.

The issue is simple to explain:
1) The WinMo device has a local time and date within DST
2) You use SetSystemTime to set a new system time and date outside the DST frame
3) The local time changes but DST is still applied!

In concrete example:

GetTimeZoneInformation:
 DaylightSaving-Time:

Bias -60  Daylight-Name: W  Daylight-Bias: -60 Standard-Name: W  Standard-Bias: 0
Standard-Date: 00/10/05 03:00:00, Daylight-Date: 00/03/05 02:00:00

1. Set time inside DST +++++++++++
+++++++++++ TstSetTime ++++++++++++
SetSystemTime:  2010/09/21 10:32:00
GetLocalTime:   2010/09/21 12:32:00
GetSystemTime:  2010/09/21 10:32:00

------------ TstSetTime ------------
2. Set time outside DST ----------
+++++++++++ TstSetTime ++++++++++++
SetSystemTime:  2010/10/31 01:50:00
GetLocalTime:   2010/10/31 03:50:00
GetSystemTime:  2010/10/31 01:50:00

As you can see, local time is 2 hours of, although the data/time is outside DST. You can also see, that the device is in GMT+1 time zone.

Ah, you say this is a known issue. OK, lets do it the microsoft way and Sleep() and set system time again:

3. SLEEP...
4. Set time outside DST 2nd CALL ----------
+++++++++++ TstSetTime ++++++++++++
SetSystemTime:  2010/10/31 01:50:00
GetLocalTime:   2010/10/31 02:50:00
GetSystemTime:  2010/10/31 01:50:00

You are right, now the local time is correct.

Ok, not clear but a workaround. Now go on and set time back into DST:

------------ TstSetTime ------------
5. Set time back inside DST +++++++++++
+++++++++++ TstSetTime ++++++++++++
SetSystemTime:  2010/09/21 10:32:00
GetLocalTime:   2010/09/21 11:32:00
GetSystemTime:  2010/09/21 10:32:00

Again a fault. This time DST is not applied although the date is within DST frame.

Doing the same in Compact Framework is more worst and you will only get valid results with disabled DST.

The best workaround is to disable DST BEFORE you change the system time and then restore DST after changing the system time.

.Net code snippets:

        private DateTime startDateTime = DateTime.Parse("2010/9/24 11:42:00");

        [DllImport("coredll.dll", SetLastError = true)]
        static extern Int32 GetLastError();

        [DllImport("coredll.dll", SetLastError = true)]
        static extern bool SetSystemTime(ref SYSTEMTIME time);
        [DllImport("coredll.dll", SetLastError = true)]
        static extern void GetSystemTime(out SYSTEMTIME lpSystemTime);

        [DllImport("coredll.dll")]
        static extern bool SetTimeZoneInformation([In] ref TIME_ZONE_INFORMATION lpTimeZoneInformation);
        [DllImport("coredll.dll", CharSet = CharSet.Auto)]
        private static extern int GetTimeZoneInformation(out TIME_ZONE_INFORMATION lpTimeZoneInformation);

        private const int TIME_ZONE_ID_UNKNOWN = 0;
        private const int TIME_ZONE_ID_STANDARD = 1;
        private const int TIME_ZONE_ID_DAYLIGHT = 2;

        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct SYSTEMTIME
        {
            public short wYear;
            public short wMonth;
            public short wDayOfWeek;
            public short wDay;
            public short wHour;
            public short wMinute;
            public short wSecond;
            public short wMilliseconds;
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct TIME_ZONE_INFORMATION
        {
            public int bias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string standardName;
            public SYSTEMTIME standardDate;
            public int standardBias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string daylightName;
            public SYSTEMTIME daylightDate;
            public int daylightBias;
        }
...
        private bool disableDST(TIME_ZONE_INFORMATION tzi){
            //set wMonth in standardDate to zero
            SYSTEMTIME stStd;
            stStd=tzi.standardDate;
            stStd.wMonth=0;
            //set wMonth in daylightDate to zero
            SYSTEMTIME stDST;
            stDST=tzi.daylightDate;
            stDST.wMonth=0;

            tzi.daylightDate=stDST;
            tzi.standardDate=stStd;
            bool bRes = SetTimeZoneInformation(ref tzi);
            if (bRes)
                addText("*** Disabling DST OK***");
            else
                addText("*** Disabling DST failed***");
            return bRes;
        }

Possibly a good information for all that do TimeSync with a server.

5 Comments

  1. zerodtkjoe says:

    Thanks for the info

  2. Habib says:

    It is not a bug, it is the expected working method according to the documentation:

    “The system uses UTC internally. Therefore, when you call SetLocalTime, the system uses the current time zone information to perform the conversion, including the daylight saving time setting. Note that the system uses the daylight saving time setting of the current time, not the new time you are setting. Therefore, to ensure the correct result, call SetLocalTime a second time, now that the first call has updated the daylight saving time setting.”

  3. Chris S. says:

    I encountered a similar bug with SetSystemTime on Windows CE 6.0. I have the latest update from March 2014.

    We had a similar issue and it might affect you as well. When you call SetSystemTime on Windows CE 6.0 and the time is set backward, it will trigger the DST transition code even if the resulting time change does not cross a DST boundary. This messes with the internal tracking variable that tracks whether or not the system thinks it is in DST or not. The result is that sometimes DST transitions will not work. The spring transition may work but the fall transition may not work.

    The solution is to call SetLocalTime instead of SetSystemTime. SetLocalTime does not trigger the DST transition code. We tried this in our Windows CE devices and our DST transitions work properly now.

    In addition, you have to call SetLocalTime twice in order to ensure that changing the time across a DST boundary works correctly.

  4. josef says:

    Hello

    many thanks for the valuable information about using SetLocalTime.

    regards

    ~Josef

  5. wsct says:

    I did it like this and it works perfectly (thanks for the information!):

    TIME_ZONE_INFORMATION tzi_old;
    TIME_ZONE_INFORMATION tzi;

    GetTimeZoneInformation(out tzi); // Get current time zone
    GetTimeZoneInformation(out tzi_old); // Get current time zone in a second var
    disableDST(tzi); // Disable DST in current time zone

    System.Threading.Thread.Sleep(1000); // Sleep

    SetSysTime(dt_time.AddHours(addHourSystem)); // Set system time

    System.Threading.Thread.Sleep(1000); // Sleep

    SetLocTime(dt_time.AddHours(addHourLocal)); // Set local time

    System.Threading.Thread.Sleep(1000); // Sleep

    SetTimeZoneInformation(ref tzi_old); // Restore old time zone with enabled DST

Leave a Reply