time.c six times: RtlTimeToTimeFields, RtlTimeFieldsToTime, SystemTimeToFileTime, mktime

Rein Klazes rklazes at xs4all.nl
Fri Nov 5 09:46:06 CST 2004


Hi,

Changelog:
	dlls/ntdll	: time.c
	dlls/ntdll/tests : time.c
	dlls/kernel	: time.c
	dlls/kernel/tests : time.c
	dlls/msvcrt	: time.c
	dlls/msvcrt/tests : time.c

	- 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.

Rein.
-- 
Rein Klazes
rklazes at xs4all.nl
-------------- next part --------------
--- wine/dlls/ntdll/time.c	2004-11-03 08:41:25.000000000 +0100
+++ mywine/dlls/ntdll/time.c	2004-11-05 16:17:52.000000000 +0100
@@ -338,23 +338,11 @@ static const int MonthLengths[2][MONSPER
 	{ 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;
 }
 
 /***********************************************************************
--- wine/dlls/ntdll/tests/time.c	2004-10-30 08:54:46.000000000 +0200
+++ mywine/dlls/ntdll/tests/time.c	2004-11-03 17:26:01.000000000 +0100
@@ -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,5
 
 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
 }
--- wine/dlls/kernel/time.c	2004-10-30 08:54:35.000000000 +0200
+++ mywine/dlls/kernel/time.c	2004-11-05 16:35:54.000000000 +0100
@@ -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 
     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;
--- wine/dlls/kernel/tests/time.c	2004-10-30 08:54:39.000000000 +0200
+++ mywine/dlls/kernel/tests/time.c	2004-11-04 22:08:29.000000000 +0100
@@ -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()
--- wine/dlls/msvcrt/time.c	2004-10-28 08:36:13.000000000 +0200
+++ mywine/dlls/msvcrt/time.c	2004-11-05 16:17:55.000000000 +0100
@@ -30,6 +30,7 @@
 #endif
 
 #include "msvcrt.h"
+#include "limits.h"
 #include "winbase.h"
 #include "wine/debug.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; 
 }
 
 /*********************************************************************
--- wine/dlls/msvcrt/tests/time.c	2004-10-26 08:25:34.000000000 +0200
+++ mywine/dlls/msvcrt/tests/time.c	2004-11-04 18:59:50.000000000 +0100
@@ -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"):""));


More information about the wine-patches mailing list