- RtlTimeFieldsToTime should not normalize the time fields

structure. Instead return error when it is given an unormalized
  date.
- Use better algorithms for RtlTimeToTimeFields and
  RtlTimeFieldsToTime. RtlTimeToTimeFields is about 3 times faster.
- Add tests for RtlTimeFieldsToTime.
- SystemTimeToFileTime must fail if RtlTimeFieldsToTime fails. Users
  of SystemTimeToFileTime must do likewise.
- Remove a todo_wine from SystemTimeToFileTime tests.
- Since msvcrt.mktime must accept unnormalized dates, it cannot use
  SystemTimeToFileTime and do the calculations itself.
- Add some tests for mktime accepting unnormalized dates.
This commit is contained in:
Rein Klazes 2004-11-06 03:53:53 +00:00 committed by Alexandre Julliard
parent 45eba51461
commit dceae02d73
6 changed files with 217 additions and 113 deletions

View File

@ -136,7 +136,6 @@ static void test_invalid_arg()
ok( (ft.dwHighDateTime==NEWYEAR_1980_HI) && (ft.dwLowDateTime==NEWYEAR_1980_LO),
"filetime for 1/1/80 00:00:00 was %08lx %08lx\n", ft.dwHighDateTime, ft.dwLowDateTime);
todo_wine {
/* now check SystemTimeToFileTime */
memset(&ft,0,sizeof ft);
@ -155,16 +154,15 @@ static void test_invalid_arg()
/* with a bad hour */
SETUP_1980(st)
st.wHour = 25;
st.wHour = 24;
ok( !SystemTimeToFileTime(&st, &ft), "bad hour\n");
/* with a bad minute */
SETUP_1980(st)
st.wMinute = 61;
st.wMinute = 60;
ok( !SystemTimeToFileTime(&st, &ft), "bad minute\n");
}
}
void test_GetTimeZoneInformation()

View File

@ -277,7 +277,8 @@ BOOL WINAPI SetLocalTime(
LARGE_INTEGER st, st2;
NTSTATUS status;
SystemTimeToFileTime( systime, &ft );
if( !SystemTimeToFileTime( systime, &ft ))
return FALSE;
st.u.LowPart = ft.dwLowDateTime;
st.u.HighPart = ft.dwHighDateTime;
RtlLocalTimeToSystemTime( &st, &st2 );
@ -329,7 +330,8 @@ BOOL WINAPI SetSystemTime(
LARGE_INTEGER t;
NTSTATUS status;
SystemTimeToFileTime( systime, &ft );
if( !SystemTimeToFileTime( systime, &ft ))
return FALSE;
t.u.LowPart = ft.dwLowDateTime;
t.u.HighPart = ft.dwHighDateTime;
if ((status = NtSetSystemTime(&t, NULL)))
@ -820,7 +822,10 @@ BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
tf.Second = syst->wSecond;
tf.Milliseconds = syst->wMilliseconds;
RtlTimeFieldsToTime(&tf, &t);
if( !RtlTimeFieldsToTime(&tf, &t)) {
SetLastError( ERROR_INVALID_PARAMETER);
return FALSE;
}
ft->dwLowDateTime = t.u.LowPart;
ft->dwHighDateTime = t.u.HighPart;
return TRUE;

View File

@ -52,7 +52,7 @@ static void test_mktime()
{
TIME_ZONE_INFORMATION tzinfo;
DWORD res = GetTimeZoneInformation(&tzinfo);
struct tm my_tm;
struct tm my_tm, sav_tm;
time_t nulltime, local_time;
char TZ_env[256];
int secs;
@ -73,10 +73,71 @@ static void test_mktime()
my_tm.tm_year = 70;
my_tm.tm_mon = 0;
my_tm.tm_isdst= 0;
sav_tm = my_tm;
local_time = mktime(&my_tm);
ok(((DWORD)local_time == SECSPERDAY), "mktime returned 0x%08lx\n",(DWORD)local_time);
/* now test some unnormalized struct tm's */
my_tm = sav_tm;
my_tm.tm_sec += 60;
my_tm.tm_min -= 1;
local_time = mktime(&my_tm);
ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
my_tm.tm_sec == sav_tm.tm_sec
, "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
my_tm.tm_hour,my_tm.tm_sec,
sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
sav_tm.tm_hour,sav_tm.tm_sec);
my_tm = sav_tm;
my_tm.tm_min -= 60;
my_tm.tm_hour += 1;
local_time = mktime(&my_tm);
ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
my_tm.tm_sec == sav_tm.tm_sec
, "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
my_tm.tm_hour,my_tm.tm_sec,
sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
sav_tm.tm_hour,sav_tm.tm_sec);
my_tm = sav_tm;
my_tm.tm_mon -= 12;
my_tm.tm_year += 1;
local_time = mktime(&my_tm);
ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
my_tm.tm_sec == sav_tm.tm_sec
, "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
my_tm.tm_hour,my_tm.tm_sec,
sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
sav_tm.tm_hour,sav_tm.tm_sec);
my_tm = sav_tm;
my_tm.tm_mon += 12;
my_tm.tm_year -= 1;
local_time = mktime(&my_tm);
ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
my_tm.tm_sec == sav_tm.tm_sec
, "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
my_tm.tm_hour,my_tm.tm_sec,
sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
sav_tm.tm_hour,sav_tm.tm_sec);
/* now a bad time example */
my_tm = sav_tm;
my_tm.tm_year -= 1;
local_time = mktime(&my_tm);
ok((local_time == -1), "(bad time) mktime returned 0x%08lx\n",(DWORD)local_time);
my_tm = sav_tm;
/* TEST that we are independent from the TZ variable */
/*Argh, msvcrt doesn't have setenv() */
_snprintf(TZ_env,255,"TZ=%s",(getenv("TZ")?getenv("TZ"):""));

View File

@ -28,6 +28,7 @@
#ifdef HAVE_SYS_TIMES_H
# include <sys/times.h>
#endif
#include <limits.h>
#include "msvcrt.h"
#include "winbase.h"
@ -61,29 +62,72 @@ static struct MSVCRT_tm tm;
*/
MSVCRT_time_t MSVCRT_mktime(struct MSVCRT_tm *t)
{
MSVCRT_time_t secs;
SYSTEMTIME st;
FILETIME lft, uft;
ULONGLONG time;
MSVCRT_time_t secs;
FILETIME lft, uft;
ULONGLONG time;
struct MSVCRT_tm ts;
int cleaps, day;
st.wMilliseconds = 0;
st.wSecond = t->tm_sec;
st.wMinute = t->tm_min;
st.wHour = t->tm_hour;
st.wDay = t->tm_mday;
st.wMonth = t->tm_mon + 1;
st.wYear = t->tm_year + 1900;
ts=*t;
/* to prevent arithmetic overflows put constraints on some fields */
/* whether the effective date falls in the 1970-2038 time period */
/* will be tested later */
/* BTW, I have no idea what limits native msvcrt has. */
if ( ts.tm_year < 0 || ts.tm_year > 140 ||
ts.tm_mon < -840 || ts.tm_mon > 840 ||
ts.tm_mday < -20160 || ts.tm_mday > 20160 ||
ts.tm_hour < -484000 || ts.tm_hour > 484000 ||
ts.tm_min < -29000000 || ts.tm_min > 29000000 )
return -1;
/* normalize the tm month fields */
if( ts.tm_mon > 11) { ts.tm_year += ts.tm_mon / 12; ts.tm_mon %= 12; }
if( ts.tm_mon < 0) {
int dy = (11 - ts.tm_mon) / 12;
ts.tm_year -= dy;
ts.tm_mon += dy * 12;
}
/* now calculate a day count from the date
* First start counting years from March. This way the leap days
* are added at the end of the year, not somewhere in the middle.
* Formula's become so much less complicate that way.
* To convert: add 12 to the month numbers of Jan and Feb, and
* take 1 from the year */
if(ts.tm_mon < 2) {
ts.tm_mon += 14;
ts.tm_year += 1899;
} else {
ts.tm_mon += 2;
ts.tm_year += 1900;
}
cleaps = (3 * (ts.tm_year / 100) + 3) / 4; /* nr of "century leap years"*/
day = (36525 * ts.tm_year) / 100 - cleaps + /* year * dayperyr, corrected*/
(1959 * ts.tm_mon) / 64 + /* months * daypermonth */
ts.tm_mday - /* day of the month */
584817 ; /* zero that on 1601-01-01 */
/* done */
SystemTimeToFileTime(&st, &lft);
LocalFileTimeToFileTime(&lft, &uft);
/* convert to 100 ns ticks */
time = ((((ULONGLONG) day * 24 +
ts.tm_hour) * 60 +
ts.tm_min) * 60 +
ts.tm_sec ) * TICKSPERSEC;
lft.dwHighDateTime = (DWORD) (time >> 32);
lft.dwLowDateTime = (DWORD) time;
time = ((ULONGLONG)uft.dwHighDateTime << 32) | uft.dwLowDateTime;
secs = time / TICKSPERSEC - SECS_1601_TO_1970;
/* compute tm_wday, tm_yday and renormalize the other fields of the
* tm structure */
if( MSVCRT_localtime( &secs)) *t = tm;
LocalFileTimeToFileTime(&lft, &uft);
return secs;
time = ((ULONGLONG)uft.dwHighDateTime << 32) | uft.dwLowDateTime;
time /= TICKSPERSEC;
if( time < SECS_1601_TO_1970 || time > (SECS_1601_TO_1970 + INT_MAX))
return -1;
secs = time - SECS_1601_TO_1970;
/* compute tm_wday, tm_yday and renormalize the other fields of the
* tm structure */
if( MSVCRT_localtime( &secs)) *t = tm;
return secs;
}
/*********************************************************************

View File

@ -27,6 +27,7 @@
#define SECSPERDAY 86400
static VOID (WINAPI *pRtlTimeToTimeFields)( const LARGE_INTEGER *liTime, PTIME_FIELDS TimeFields) ;
static VOID (WINAPI *pRtlTimeFieldsToTime)( PTIME_FIELDS TimeFields, PLARGE_INTEGER Time) ;
static const int MonthLengths[2][12] =
{
@ -44,7 +45,7 @@ TIME_FIELDS tftest = {1889,12,31,23,59,59,0,0};
static void test_pRtlTimeToTimeFields()
{
LARGE_INTEGER litime ;
LARGE_INTEGER litime , liresult;
TIME_FIELDS tfresult;
int i=0;
litime.QuadPart = ((ULONGLONG)0x0144017a << 32) | 0xf0b0a980;
@ -59,6 +60,12 @@ static void test_pRtlTimeToTimeFields()
tftest.Hour, tftest.Minute,tftest.Second,
tfresult.Year, tfresult.Month, tfresult.Day,
tfresult.Hour, tfresult.Minute, tfresult.Second);
/* test the inverse */
pRtlTimeFieldsToTime( &tfresult, &liresult);
ok( liresult.QuadPart == litime.QuadPart," TimeFieldsToTime failed on %d-%d-%d %d:%d:%d. Error is %d ticks\n",
tfresult.Year, tfresult.Month, tfresult.Day,
tfresult.Hour, tfresult.Minute, tfresult.Second,
(int) (liresult.QuadPart - litime.QuadPart) );
/* one second later is beginning of next month */
litime.QuadPart += TICKSPERSEC ;
pRtlTimeToTimeFields( &litime, &tfresult);
@ -71,6 +78,12 @@ static void test_pRtlTimeToTimeFields()
tftest.Month % 12 + 1, 1, 0, 0, 0,
tfresult.Year, tfresult.Month, tfresult.Day,
tfresult.Hour, tfresult.Minute, tfresult.Second);
/* test the inverse */
pRtlTimeFieldsToTime( &tfresult, &liresult);
ok( liresult.QuadPart == litime.QuadPart," TimeFieldsToTime failed on %d-%d-%d %d:%d:%d. Error is %d ticks\n",
tfresult.Year, tfresult.Month, tfresult.Day,
tfresult.Hour, tfresult.Minute, tfresult.Second,
(int) (liresult.QuadPart - litime.QuadPart) );
/* advance to the end of the month */
litime.QuadPart -= TICKSPERSEC ;
if( tftest.Month == 12) {
@ -89,7 +102,8 @@ START_TEST(time)
#ifdef __WINE_WINTERNL_H
HMODULE mod = GetModuleHandleA("ntdll.dll");
pRtlTimeToTimeFields = (void *)GetProcAddress(mod,"RtlTimeToTimeFields");
if (pRtlTimeToTimeFields)
pRtlTimeFieldsToTime = (void *)GetProcAddress(mod,"RtlTimeFieldsToTime");
if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime)
test_pRtlTimeToTimeFields();
#endif
}

View File

@ -338,23 +338,11 @@ static const int MonthLengths[2][MONSPERYEAR] =
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
static const int YearDays[2][MONSPERYEAR+1] =
{
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
static inline int IsLeapYear(int Year)
{
return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
}
static inline void NormalizeTimeFields(CSHORT *FieldToNormalize, CSHORT *CarryField,int Modulus)
{
*FieldToNormalize = (CSHORT) (*FieldToNormalize - Modulus);
*CarryField = (CSHORT) (*CarryField + 1);
}
/***********************************************************************
* NTDLL_get_server_timeout
*
@ -420,14 +408,15 @@ VOID WINAPI RtlTimeToTimeFields(
const LARGE_INTEGER *liTime,
PTIME_FIELDS TimeFields)
{
int SecondsInDay, DeltaYear;
int LeapYear, CurMonth;
int SecondsInDay;
long int cleaps, years, yearday, months;
long int Days;
LONGLONG Time = liTime->QuadPart;
LONGLONG Time;
/* Extract millisecond from time and convert time into seconds */
TimeFields->Milliseconds = (CSHORT) ((Time % TICKSPERSEC) / TICKSPERMSEC);
Time = Time / TICKSPERSEC;
TimeFields->Milliseconds =
(CSHORT) (( liTime->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
Time = liTime->QuadPart / TICKSPERSEC;
/* The native version of RtlTimeToTimeFields does not take leap seconds
* into account */
@ -445,32 +434,27 @@ VOID WINAPI RtlTimeToTimeFields(
/* compute day of week */
TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
/* compute year */
/* FIXME: handle calendar modifications */
TimeFields->Year = EPOCHYEAR;
DeltaYear = Days / DAYSPERQUADRICENTENNIUM;
TimeFields->Year += DeltaYear * 400;
Days -= DeltaYear * DAYSPERQUADRICENTENNIUM;
DeltaYear = Days / DAYSPERNORMALCENTURY;
if( DeltaYear > 3) DeltaYear = 3; /* fix 31 Dec of 2000 and every
400 years after that */
TimeFields->Year += DeltaYear * 100;
Days -= DeltaYear * DAYSPERNORMALCENTURY;
DeltaYear = Days / DAYSPERNORMALQUADRENNIUM;
TimeFields->Year += DeltaYear * 4;
Days -= DeltaYear * DAYSPERNORMALQUADRENNIUM;
DeltaYear = Days / DAYSPERNORMALYEAR;
if( DeltaYear > 3) DeltaYear = 3; /* fix 31 Dec of every leap year */
TimeFields->Year += DeltaYear;
Days -= DeltaYear * DAYSPERNORMALYEAR;
LeapYear = IsLeapYear(TimeFields->Year);
/* Compute month of year */
CurMonth = 1;
while (Days >= YearDays[LeapYear][CurMonth]) CurMonth++;
TimeFields->Day = Days - YearDays[LeapYear][CurMonth-1] + 1;
TimeFields->Month = CurMonth;
/* compute year, month and day of month. */
cleaps=( 3 * ((4 * Days + 1227) / DAYSPERQUADRICENTENNIUM) + 3 ) / 4;
Days += 28188 + cleaps;
years = (20 * Days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
yearday = Days - (years * DAYSPERNORMALQUADRENNIUM)/4;
months = (64 * yearday) / 1959;
/* the result is based on a year starting on March.
* To convert take 12 from Januari and Februari and
* increase the year by one. */
if( months < 14 ) {
TimeFields->Month = months - 1;
TimeFields->Year = years + 1524;
} else {
TimeFields->Month = months - 13;
TimeFields->Year = years + 1525;
}
/* calculation of day of month is based on the wonderful
* sequence of INT( n * 30.6): it reproduces the
* 31-30-31-30-31-31 month lengths exactly for small n's */
TimeFields->Day = yearday - (1959 * months) / 64 ;
return;
}
/******************************************************************************
@ -490,51 +474,49 @@ BOOLEAN WINAPI RtlTimeFieldsToTime(
PTIME_FIELDS tfTimeFields,
PLARGE_INTEGER Time)
{
int CurYear, DeltaYear;
LONGLONG rcTime;
TIME_FIELDS TimeFields = *tfTimeFields;
rcTime = 0;
int month, year, cleaps, day;
/* FIXME: normalize the TIME_FIELDS structure here */
while (TimeFields.Second >= SECSPERMIN)
{ NormalizeTimeFields(&TimeFields.Second, &TimeFields.Minute, SECSPERMIN);
}
while (TimeFields.Minute >= MINSPERHOUR)
{ NormalizeTimeFields(&TimeFields.Minute, &TimeFields.Hour, MINSPERHOUR);
}
while (TimeFields.Hour >= HOURSPERDAY)
{ NormalizeTimeFields(&TimeFields.Hour, &TimeFields.Day, HOURSPERDAY);
}
while (TimeFields.Day > MonthLengths[IsLeapYear(TimeFields.Year)][TimeFields.Month - 1])
{ NormalizeTimeFields(&TimeFields.Day, &TimeFields.Month,
MonthLengths[IsLeapYear(TimeFields.Year)][TimeFields.Month - 1]);
}
while (TimeFields.Month > MONSPERYEAR)
{ NormalizeTimeFields(&TimeFields.Month, &TimeFields.Year, MONSPERYEAR);
}
/* No, native just returns 0 (error) if the fields are not */
if( tfTimeFields->Milliseconds< 0 || tfTimeFields->Milliseconds > 999 ||
tfTimeFields->Second < 0 || tfTimeFields->Second > 59 ||
tfTimeFields->Minute < 0 || tfTimeFields->Minute > 59 ||
tfTimeFields->Hour < 0 || tfTimeFields->Hour > 23 ||
tfTimeFields->Month < 1 || tfTimeFields->Month > 12 ||
tfTimeFields->Day < 1 ||
tfTimeFields->Day > MonthLengths
[ tfTimeFields->Month ==2 || IsLeapYear(tfTimeFields->Year)]
[ tfTimeFields->Month - 1] ||
tfTimeFields->Year < 1601 )
return FALSE;
/* FIXME: handle calendar corrections here */
CurYear = TimeFields.Year - EPOCHYEAR;
DeltaYear = CurYear / 400;
CurYear -= DeltaYear * 400;
rcTime += DeltaYear * DAYSPERQUADRICENTENNIUM;
DeltaYear = CurYear / 100;
CurYear -= DeltaYear * 100;
rcTime += DeltaYear * DAYSPERNORMALCENTURY;
DeltaYear = CurYear / 4;
CurYear -= DeltaYear * 4;
rcTime += DeltaYear * DAYSPERNORMALQUADRENNIUM;
rcTime += CurYear * DAYSPERNORMALYEAR;
rcTime += YearDays[IsLeapYear(TimeFields.Year)][TimeFields.Month - 1];
rcTime += TimeFields.Day - 1;
rcTime *= SECSPERDAY;
rcTime += TimeFields.Hour * SECSPERHOUR + TimeFields.Minute * SECSPERMIN + TimeFields.Second;
rcTime *= TICKSPERSEC;
rcTime += TimeFields.Milliseconds * TICKSPERMSEC;
Time->QuadPart = rcTime;
/* now calculate a day count from the date
* First start counting years from March. This way the leap days
* are added at the end of the year, not somewhere in the middle.
* Formula's become so much less complicate that way.
* To convert: add 12 to the month numbers of Jan and Feb, and
* take 1 from the year */
if(tfTimeFields->Month < 3) {
month = tfTimeFields->Month + 13;
year = tfTimeFields->Year - 1;
} else {
month = tfTimeFields->Month + 1;
year = tfTimeFields->Year;
}
cleaps = (3 * (year / 100) + 3) / 4; /* nr of "century leap years"*/
day = (36525 * year) / 100 - cleaps + /* year * dayperyr, corrected */
(1959 * month) / 64 + /* months * daypermonth */
tfTimeFields->Day - /* day of the month */
584817 ; /* zero that on 1601-01-01 */
/* done */
Time->QuadPart = (((((LONGLONG) day * HOURSPERDAY +
tfTimeFields->Hour) * MINSPERHOUR +
tfTimeFields->Minute) * SECSPERMIN +
tfTimeFields->Second ) * 1000 +
tfTimeFields->Milliseconds ) * TICKSPERMSEC;
return TRUE;
return TRUE;
}
/***********************************************************************