MORE INFORMATION
Steps to Reproduce the Behavior
- 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. - 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. - In Control Panel, double-click
Date/Time.
- In the Date/Time Properties dialog box,
click (GMT-07:00) Mountain Time (US & Canada) on the
Time Zone tab.
- Click to select the Automatically adjust clock for
daylight saving changes check box.
- 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.
- At the command prompt, run Sample.exe.
- 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.