diff --git a/win32/time.c b/win32/time.c index 2bdf91f1d5e..82c63dd40ae 100644 --- a/win32/time.c +++ b/win32/time.c @@ -14,6 +14,20 @@ DEFAULT_DEBUG_CHANNEL(win32) +/* maximum time adjustment in seconds for SetLocalTime and SetSystemTime */ +#define SETTIME_MAX_ADJUST 120 + +/* TIME_GetBias: helper function calculates delta local time from UTC */ +static int TIME_GetBias( time_t utc, int *pdaylight) +{ + struct tm *ptm = localtime(&utc); + *pdaylight = ptm->tm_isdst; /* daylight for local timezone */ + ptm = gmtime(&utc); + ptm->tm_isdst = *pdaylight; /* use local daylight, not that of Greenwich */ + return (int)(utc-mktime(ptm)); +} + + /*********************************************************************** * GetLocalTime (KERNEL32.228) */ @@ -49,20 +63,39 @@ BOOL WINAPI SetLocalTime(const SYSTEMTIME *systime) struct timeval tv; struct tm t; time_t sec; + time_t oldsec=time(NULL); + int err; /* get the number of seconds */ t.tm_sec = systime->wSecond; t.tm_min = systime->wMinute; t.tm_hour = systime->wHour; t.tm_mday = systime->wDay; - t.tm_mon = systime->wMonth; - t.tm_year = systime->wYear; + t.tm_mon = systime->wMonth - 1; + t.tm_year = systime->wYear - 1900; + t.tm_isdst = -1; sec = mktime (&t); /* set the new time */ tv.tv_sec = sec; tv.tv_usec = systime->wMilliseconds * 1000; - return !settimeofday(&tv, NULL); + + /* error and sanity check*/ + if( sec == (time_t)-1 || abs((int)(sec-oldsec)) > SETTIME_MAX_ADJUST ){ + err = 1; + SetLastError(ERROR_INVALID_PARAMETER); + } else { + err=settimeofday(&tv, NULL); /* 0 is OK, -1 is error */ + if(err == 0) + return TRUE; + SetLastError(ERROR_PRIVILEGE_NOT_HELD); + } + ERR("Cannot set time to %d/%d/%d %d:%d:%d Time adjustment %ld %s\n", + systime->wYear, systime->wMonth, systime->wDay, systime->wHour, + systime->wMinute, systime->wSecond, + sec-oldsec, err == -1 ? "No Permission" : + sec==(time_t)-1 ? "" : "is too large." ); + return FALSE; } @@ -98,24 +131,49 @@ BOOL WINAPI SetSystemTime(const SYSTEMTIME *systime) struct timeval tv; struct timezone tz; struct tm t; - time_t sec; + time_t sec, oldsec; + int dst, bias; + int err; /* call gettimeofday to get the current timezone */ gettimeofday(&tv, &tz); + oldsec=tv.tv_sec; + /* get delta local time from utc */ + bias=TIME_GetBias(oldsec,&dst); + /* get the number of seconds */ t.tm_sec = systime->wSecond; t.tm_min = systime->wMinute; t.tm_hour = systime->wHour; t.tm_mday = systime->wDay; - t.tm_mon = systime->wMonth; - t.tm_year = systime->wYear; + t.tm_mon = systime->wMonth - 1; + t.tm_year = systime->wYear - 1900; + t.tm_isdst = dst; sec = mktime (&t); + /* correct for timezone and daylight */ + sec += bias; /* set the new time */ tv.tv_sec = sec; tv.tv_usec = systime->wMilliseconds * 1000; - return !settimeofday(&tv, &tz); + + /* error and sanity check*/ + if( sec == (time_t)-1 || abs((int)(sec-oldsec)) > SETTIME_MAX_ADJUST ){ + err = 1; + SetLastError(ERROR_INVALID_PARAMETER); + } else { + err=settimeofday(&tv, NULL); /* 0 is OK, -1 is error */ + if(err == 0) + return TRUE; + SetLastError(ERROR_PRIVILEGE_NOT_HELD); + } + ERR("Cannot set time to %d/%d/%d %d:%d:%d Time adjustment %ld %s\n", + systime->wYear, systime->wMonth, systime->wDay, systime->wHour, + systime->wMinute, systime->wSecond, + sec-oldsec, err == -1 ? "No Permission" : + sec==(time_t)-1 ? "" : "is too large." ); + return FALSE; } @@ -124,21 +182,15 @@ BOOL WINAPI SetSystemTime(const SYSTEMTIME *systime) */ DWORD WINAPI GetTimeZoneInformation(LPTIME_ZONE_INFORMATION tzinfo) { - time_t gmt, lt; - struct tm *ptm; - int daylight; + time_t gmt; + int bias, daylight; memset(tzinfo, 0, sizeof(TIME_ZONE_INFORMATION)); gmt = time(NULL); - ptm=localtime(&gmt); - daylight=ptm->tm_isdst; - - ptm = gmtime(&gmt); - ptm->tm_isdst=daylight; - lt = mktime(ptm); + bias=TIME_GetBias(gmt,&daylight); - tzinfo->Bias = (lt - gmt) / 60; + tzinfo->Bias = -bias / 60; tzinfo->StandardBias = 0; tzinfo->DaylightBias = -60;