PRB: Time Function May Return Wrong UTC Time and _Ftime Function May Return Wrong DST Flag (821231)



The information in this article applies to:

  • Microsoft Visual C++
  • Microsoft Visual C++, 32-bit Editions 6.0
  • Microsoft Visual C++ .NET (2002)
  • Microsoft Visual C++ .NET (2003)

SYMPTOMS

When you use the time function in Visual C++, and the Daylight Saving Time (DST) flag is changed on the computer, the function may return 3600 seconds + Coordinated Universal Time (UTC). This behavior occurs when successive calls are made to the function within one minute.

CAUSE

When the DST flag is changed on the computer, the _ftime function may return an incorrect DST flag when successive calls to the _ftime function occur after one minute. This problem may occur while the time and the _ftime functions cache the DST flag. The GetTimeZoneInformation API uses a great deal of system resources and time. Therefore, the DST status is cached and is only updated when the time or the _ftime function is called again within one minute.

STATUS

This behavior is by design.

MORE INFORMATION

Steps to Reproduce the Behavior

  1. Paste the following code in Notepad:
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/timeb.h>
    #include <time.h>
    
    /*
     * Three values of dstflag_cache
     */
    #define MYDAYLIGHT_TIME   1
    #define MYSTANDARD_TIME   0
    #define MYUNKNOWN_TIME    -1
    
    int GetDaylightSavingFlag()
    {
        int dstF = 0;
        TIME_ZONE_INFORMATION tzinfo;
        DWORD tzstate;
    
        if ( (tzstate = GetTimeZoneInformation( &tzinfo )) != 0xFFFFFFFF )
        {
    		/*
            * Must be very careful in determining whether DST is
            * really in effect.
            */
            if ( (tzstate == TIME_ZONE_ID_DAYLIGHT) &&
                    (tzinfo.DaylightDate.wMonth != 0) &&
                    (tzinfo.DaylightBias != 0) )
                dstF= MYDAYLIGHT_TIME;
            else
            /*
            * If you are not sure, assume standard time
            */
    		dstF= MYSTANDARD_TIME;
    	}
        else
            dstF= MYUNKNOWN_TIME;
        return dstF;
    }
    
    void main(void)
    {
    
        struct _timeb timebuffer;
        long t, tPrev=0;
        FILE *fp;
    
        if((fp = fopen("C:\\junk.txt","wt") ) != NULL)
        for(;;)
        {
            _ftime(&timebuffer);
            time(&t);
            if (t != tPrev)
            {
                        int dst = GetDaylightSavingFlag();
                        tPrev = t;
                        fprintf(fp,"time()=%ld, ftime()=%ld%s\n",
    				t,timebuffer.time, (t != timebuffer.time ? "   diff" : ""));
    		    fprintf(fp, "ftime.dstFlag = %d; my.dstFlag = %d%s\n", 
    				timebuffer.dstflag, dst, (dst != timebuffer.dstflag ? "    diff" : ""));
                        fflush(fp);
            }
            Sleep(1);
        }
        fclose(fp);
    }
    Save the file as Sample.cpp.
  2. Compile the earlier code at the command prompt by using the following command:
    cl Sample.cpp
    Note Perform steps 3-7 only when your computer is running Windows 2000.
  3. In Control Panel, double-click Date/Time.
  4. In the Date/Time Properties dialog box, click (GMT-07:00) Mountain Time (US & Canada) on the Time Zone tab.
  5. Click to select the Automatically adjust clock for daylight saving changes check box.
  6. On the Date & Time tab, set the date to 6 April 2003 under Date. (For the Mountain Time Zone, the change from Standard Time to the Daylight Saving Time occurs on this date for the year 2003) In the Time box, set the time to 1:59:55 AM.
  7. At the command prompt, run Sample.exe.
  8. The output of Sample.exe (in the file C:\junk.txt) is as follows:

    Visual C++ .NET

    time()=1049619599, ftime()=1049619599
    ftime.dstFlag = 0; my.dstFlag = 0
    time()=1049619600, ftime()=1049619600
    ftime.dstFlag = 0; my.dstFlag = 0
    time()=1049619601, ftime()=1049619601
    ftime.dstFlag = 0; my.dstFlag = 1    diff
    time()=1049619602, ftime()=1049619602
    ftime.dstFlag = 0; my.dstFlag = 1    diff
    time()=1049619603, ftime()=1049619603
    ftime.dstFlag = 0; my.dstFlag = 1    diff

    Visual C++ 6.0

    time()=1049619598, ftime()=1049619598
    ftime.dstFlag = 0; my.dstFlag = 0
    time()=1049619599, ftime()=1049619599
    ftime.dstFlag = 0; my.dstFlag = 0
    time()=1049619600, ftime()=1049619600
    ftime.dstFlag = 0; my.dstFlag = 0
    time()=1049623200, ftime()=1049619600   diff
    ftime.dstFlag = 0; my.dstFlag = 1    diff
    time()=1049623201, ftime()=1049619601   diff
    ftime.dstFlag = 0; my.dstFlag = 1    diff
    time()=1049623202, ftime()=1049619602   diff
    ftime.dstFlag = 0; my.dstFlag = 1    diff
    time()=1049623203, ftime()=1049619603   diff
    ftime.dstFlag = 0; my.dstFlag = 1    diff
    Note The difference in the output of Sample.exe when you compile the code with Visual C++ 6.0 and Visual C++ .NET is the change in the implementation of the time function in Visual C++ .NET. The time function implementation in Visual C++ .NET does not implement the DST flag caching.
To understand why the _ftime function returns an incorrect DST flag in successive calls after the Daylight Savings Time settings have changed on the computer, look at the following code. The following code is an excerpt from the implementation of the _ftime function:
_CRTIMP void __cdecl _ftime (
        struct _timeb *tp
        )
{
...
        if ( (t = (time_t)(nt_time.ft_scalar / 600000000i64))
             != elapsed_minutes_cache )
        {
            ...
        }
...
}
This code implements Daylight Saving Time (DST) flag caching. The DST flag is cached because the API that provides the DST status, GetTimeZoneInformation, uses a lot of system resources. When a successive call to _ftime is made within a minute, the cached DST flag is used instead of making the GetTimeZoneInformation API call. When the DST flag is changed on your computer within one minute from successive calls to the _ftime function, the erroneous cached DST flag is returned by _ftime function.

To understand why the time function returns the incorrect UTC time in successive calls after the Daylight Savings Time settings have changed on the system, look at the following code. The following code is an excerpt from the implementation of the time function in Visual C++ 6.0:
time_t __cdecl time (
        time_t *timeptr
        )
{
       ...
       GetSystemTime( &gmt );

        if ( (gmt.wMinute == gmt_cache.wMinute) &&
             (gmt.wHour == gmt_cache.wHour) &&
             (gmt.wDay == gmt_cache.wDay) &&
             (gmt.wMonth == gmt_cache.wMonth) &&
             (gmt.wYear == gmt_cache.wYear) )
        {
            dstflag = dstflag_cache;
        }
        else
        {
            if ( (tzstate = GetTimeZoneInformation( &tzinfo )) != 0xFFFFFFFF )
            {
                ...
            }
            ...
        }
								tim = __loctotime_t( (int)loct.wYear,
                             (int)loct.wMonth,
                             (int)loct.wDay,
                             (int)loct.wHour,
                             (int)loct.wMinute,
                             (int)loct.wSecond,
                             dstflag );
        ...
}
This is similar to the code for the _ftime function. DST flag caching is implemented to prevent the overhead of calling the GetTimeZoneInformation API. When a successive call is made to the time function within one minute, the DST flag that is stored in the cache is used instead of getting the flag from the GetTimeZoneInformation function. When the DST flag is changed on the computer within the one minute between successive time function calls, the erroneous cached DST flag is passed to the __loctotime_t function. The excerpt of the __loctotime_t function code that returns the incorrect UTC time is as follows:
time_t __cdecl __loctotime_t (
        int yr,         /* 0 based */
        int mo,         /* 1 based */
        int dy,         /* 1 based */
        int hr,
        int mn,
        int sc,
        int dstflag )
{
    ...
    if ( (dstflag == 1) || ((dstflag == -1) && _daylight &&
                                _isindst(&tb)) )
                tmptim += _dstbias;
    ...
}
Because the DST flag that is passed in the time function to the __loctotime_t function is incorrect, the line of code (that is shown earlier in this article) in the __loctotime_t function is never run. This line corrects the time that is returned when the DST flag is set on the computer.

The problem with the time function is only found in Visual C++ 6.0 and does not appear in Visual C++ .NET. The time function returns the correct value in Visual C++ .NET because the time function does not cache the DST flag in Visual C++ .NET.

REFERENCES

For additional information about the time and the _ftime functions, visit the following Microsoft Developer Network (MSDN) Web sites:

Modification Type:MajorLast Reviewed:8/28/2003
Keywords:kbConly kbAPI kbprb KB821231 kbAudDeveloper