From 136fa03fbfe84f4f68818413b8dde33934c4cbe7 Mon Sep 17 00:00:00 2001 From: Jon Griffiths Date: Fri, 14 Nov 2003 21:01:36 +0000 Subject: [PATCH] Implement the non-variant date conversions correctly, without going through 'struct tm' first. Handle negative & 'rolling' dates too. --- dlls/oleaut32/variant.c | 710 +++++++++++++++++++++++----------------- 1 file changed, 416 insertions(+), 294 deletions(-) diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index 64ece928d01..25cec3e483c 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -2,6 +2,10 @@ * VARIANT * * Copyright 1998 Jean-Claude Cote + * Copyright 2003 Jon Griffiths + * The alorithm for conversion from Julian days to day/month/year is based on + * that devised by Henry Fliegel, as implemented in PostgreSQL, which is + * Copyright 1994-7 Regents of the University of California * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -4206,27 +4210,427 @@ error: return DISP_E_TYPEMISMATCH; } +/* Date Conversions */ + +#define IsLeapYear(y) (((y % 4) == 0) && (((y % 100) != 0) || ((y % 400) == 0))) + +/* Convert a VT_DATE value to a Julian Date */ +static inline int VARIANT_JulianFromDate(int dateIn) +{ + int julianDays = dateIn; + + julianDays -= DATE_MIN; /* Convert to + days from 1 Jan 100 AD */ + julianDays += 1757585; /* Convert to + days from 23 Nov 4713 BC (Julian) */ + return julianDays; +} + +/* Convert a Julian Date to a VT_DATE value */ +static inline int VARIANT_DateFromJulian(int dateIn) +{ + int julianDays = dateIn; + + julianDays -= 1757585; /* Convert to + days from 1 Jan 100 AD */ + julianDays += DATE_MIN; /* Convert to +/- days from 1 Jan 1899 AD */ + return julianDays; +} + +/* Convert a Julian date to Day/Month/Year - from PostgreSQL */ +static inline void VARIANT_DMYFromJulian(int jd, USHORT *year, USHORT *month, USHORT *day) +{ + int j, i, l, n; + + l = jd + 68569; + n = l * 4 / 146097; + l -= (n * 146097 + 3) / 4; + i = (4000 * (l + 1)) / 1461001; + l += 31 - (i * 1461) / 4; + j = (l * 80) / 2447; + *day = l - (j * 2447) / 80; + l = j / 11; + *month = (j + 2) - (12 * l); + *year = 100 * (n - 49) + i + l; +} + +/* Convert Day/Month/Year to a Julian date - from PostgreSQL */ +static inline double VARIANT_JulianFromDMY(USHORT year, USHORT month, USHORT day) +{ + int m12 = (month - 14) / 12; + + return ((1461 * (year + 4800 + m12)) / 4 + (367 * (month - 2 - 12 * m12)) / 12 - + (3 * ((year + 4900 + m12) / 100)) / 4 + day - 32075); +} + +/* Macros for accessing DOS format date/time fields */ +#define DOS_YEAR(x) (1980 + (x >> 9)) +#define DOS_MONTH(x) ((x >> 5) & 0xf) +#define DOS_DAY(x) (x & 0x1f) +#define DOS_HOUR(x) (x >> 11) +#define DOS_MINUTE(x) ((x >> 5) & 0x3f) +#define DOS_SECOND(x) ((x & 0x1f) << 1) +/* Create a DOS format date/time */ +#define DOS_DATE(d,m,y) (d | (m << 5) | ((y-1980) << 9)) +#define DOS_TIME(h,m,s) ((s >> 1) | (m << 5) | (h << 11)) + +/* Roll a date forwards or backwards to correct it */ +static HRESULT VARIANT_RollUdate(UDATE *lpUd) +{ + static const BYTE days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + TRACE("Raw date: %d/%d/%d %d:%d:%d\n", lpUd->st.wDay, lpUd->st.wMonth, + lpUd->st.wYear, lpUd->st.wHour, lpUd->st.wMinute, lpUd->st.wSecond); + + /* Years < 100 are treated as 1900 + year */ + if (lpUd->st.wYear < 100) + lpUd->st.wYear += 1900; + + if (!lpUd->st.wMonth) + { + /* Roll back to December of the previous year */ + lpUd->st.wMonth = 12; + lpUd->st.wYear--; + } + else while (lpUd->st.wMonth > 12) + { + /* Roll forward the correct number of months */ + lpUd->st.wYear++; + lpUd->st.wMonth -= 12; + } + + if (lpUd->st.wYear > 9999 || lpUd->st.wHour > 23 || + lpUd->st.wMinute > 59 || lpUd->st.wSecond > 59) + return E_INVALIDARG; /* Invalid values */ + + if (!lpUd->st.wDay) + { + /* Roll back the date one day */ + if (lpUd->st.wMonth == 1) + { + /* Roll back to December 31 of the previous year */ + lpUd->st.wDay = 31; + lpUd->st.wMonth = 12; + lpUd->st.wYear--; + } + else + { + lpUd->st.wMonth--; /* Previous month */ + if (lpUd->st.wMonth == 2 && IsLeapYear(lpUd->st.wYear)) + lpUd->st.wDay = 29; /* Februaury has 29 days on leap years */ + else + lpUd->st.wDay = days[lpUd->st.wMonth]; /* Last day of the month */ + } + } + else if (lpUd->st.wDay > 28) + { + int rollForward = 0; + + /* Possibly need to roll the date forward */ + if (lpUd->st.wMonth == 2 && IsLeapYear(lpUd->st.wYear)) + rollForward = lpUd->st.wDay - 29; /* Februaury has 29 days on leap years */ + else + rollForward = lpUd->st.wDay - days[lpUd->st.wMonth]; + + if (rollForward > 0) + { + lpUd->st.wDay = rollForward; + lpUd->st.wMonth++; + if (lpUd->st.wMonth > 12) + { + lpUd->st.wMonth = 1; /* Roll forward into January of the next year */ + lpUd->st.wYear++; + } + } + } + TRACE("Rolled date: %d/%d/%d %d:%d:%d\n", lpUd->st.wDay, lpUd->st.wMonth, + lpUd->st.wYear, lpUd->st.wHour, lpUd->st.wMinute, lpUd->st.wSecond); + return S_OK; +} + /********************************************************************** * DosDateTimeToVariantTime [OLEAUT32.14] - * Convert dos representation of time to the date and time representation - * stored in a variant. + * + * Convert a Dos format date and time into variant VT_DATE format. + * + * PARAMS + * wDosDate [I] Dos format date + * wDosTime [I] Dos format time + * pDateOut [O] Destination for VT_DATE format + * + * RETURNS + * Success: TRUE. pDateOut contains the converted time. + * Failure: FALSE, if wDosDate or wDosTime are invalid (see notes). + * + * NOTES + * - Dos format dates can only hold dates from 1-Jan-1980 to 31-Dec-2099. + * - Dos format times are accurate to only 2 second precision. + * - The format of a Dos Date is: + *| Bits Values Meaning + *| ---- ------ ------- + *| 0-4 1-31 Day of the week. 0 rolls back one day. A value greater than + *| the days in the month rolls forward the extra days. + *| 5-8 1-12 Month of the year. 0 rolls back to December of the previous + *| year. 13-15 are invalid. + *| 9-15 0-119 Year based from 1980 (Max 2099). 120-127 are invalid. + * - The format of a Dos Time is: + *| Bits Values Meaning + *| ---- ------ ------- + *| 0-4 0-29 Seconds/2. 30 and 31 are invalid. + *| 5-10 0-59 Minutes. 60-63 are invalid. + *| 11-15 0-23 Hours (24 hour clock). 24-32 are invalid. */ INT WINAPI DosDateTimeToVariantTime(USHORT wDosDate, USHORT wDosTime, - DATE *pvtime) + double *pDateOut) { - struct tm t; + UDATE ud; - TRACE("( 0x%x, 0x%x, %p ), stub\n", wDosDate, wDosTime, pvtime ); + TRACE("(0x%x(%d/%d/%d),0x%x(%d:%d:%d),%p)\n", + wDosDate, DOS_YEAR(wDosDate), DOS_MONTH(wDosDate), DOS_DAY(wDosDate), + wDosTime, DOS_HOUR(wDosTime), DOS_MINUTE(wDosTime), DOS_SECOND(wDosTime), + pDateOut); - t.tm_sec = (wDosTime & 0x001f) * 2; - t.tm_min = (wDosTime & 0x07e0) >> 5; - t.tm_hour = (wDosTime & 0xf800) >> 11; + ud.st.wYear = DOS_YEAR(wDosDate); + ud.st.wMonth = DOS_MONTH(wDosDate); + if (ud.st.wYear > 2099 || ud.st.wMonth > 12) + return FALSE; + ud.st.wDay = DOS_DAY(wDosDate); + ud.st.wHour = DOS_HOUR(wDosTime); + ud.st.wMinute = DOS_MINUTE(wDosTime); + ud.st.wSecond = DOS_SECOND(wDosTime); + ud.st.wDayOfWeek = ud.st.wMilliseconds = 0; - t.tm_mday = (wDosDate & 0x001f); - t.tm_mon = (wDosDate & 0x01e0) >> 5; - t.tm_year = ((wDosDate & 0xfe00) >> 9) + 1980; + return !VarDateFromUdate(&ud, 0, pDateOut); +} - return TmToDATE( &t, pvtime ); +/********************************************************************** + * VariantTimeToDosDateTime [OLEAUT32.13] + * + * Convert a variant format date into a Dos format date and time. + * + * dateIn [I] VT_DATE time format + * pwDosDate [O] Destination for Dos format date + * pwDosTime [O] Destination for Dos format time + * + * RETURNS + * Success: TRUE. pwDosDate and pwDosTime contains the converted values. + * Failure: FALSE, if dateIn cannot be represented in Dos format. + * + * NOTES + * See DosDateTimeToVariantTime() for Dos format details and bugs. + */ +INT WINAPI VariantTimeToDosDateTime(double dateIn, USHORT *pwDosDate, USHORT *pwDosTime) +{ + UDATE ud; + + TRACE("(%g,%p,%p)\n", dateIn, pwDosDate, pwDosTime); + + if (FAILED(VarUdateFromDate(dateIn, 0, &ud))) + return FALSE; + + if (ud.st.wYear < 1980 || ud.st.wYear > 2099) + return FALSE; + + *pwDosDate = DOS_DATE(ud.st.wDay, ud.st.wMonth, ud.st.wYear); + *pwDosTime = DOS_TIME(ud.st.wHour, ud.st.wMinute, ud.st.wSecond); + + TRACE("Returning 0x%x(%d/%d/%d), 0x%x(%d:%d:%d)\n", + *pwDosDate, DOS_YEAR(*pwDosDate), DOS_MONTH(*pwDosDate), DOS_DAY(*pwDosDate), + *pwDosTime, DOS_HOUR(*pwDosTime), DOS_MINUTE(*pwDosTime), DOS_SECOND(*pwDosTime)); + return TRUE; +} + +/*********************************************************************** + * SystemTimeToVariantTime [OLEAUT32.184] + * + * Convert a System format date and time into variant VT_DATE format. + * + * PARAMS + * lpSt [I] System format date and time + * pDateOut [O] Destination for VT_DATE format date + * + * RETURNS + * Success: TRUE. *pDateOut contains the converted value. + * Failure: FALSE, if lpSt cannot be represented in VT_DATE format. + */ +INT WINAPI SystemTimeToVariantTime(LPSYSTEMTIME lpSt, double *pDateOut) +{ + UDATE ud; + + TRACE("(%p->%d/%d/%d %d:%d:%d,%p)\n", lpSt, lpSt->wDay, lpSt->wMonth, + lpSt->wYear, lpSt->wHour, lpSt->wMinute, lpSt->wSecond, pDateOut); + + if (lpSt->wMonth > 12) + return FALSE; + + memcpy(&ud.st, lpSt, sizeof(ud.st)); + return !VarDateFromUdate(&ud, 0, pDateOut); +} + +/*********************************************************************** + * VariantTimeToSystemTime [OLEAUT32.185] + * + * Convert a variant VT_DATE into a System format date and time. + * + * PARAMS + * datein [I] Variant VT_DATE format date + * lpSt [O] Destination for System format date and time + * + * RETURNS + * Success: TRUE. *lpSt contains the converted value. + * Failure: FALSE, if dateIn is too large or small. + */ +INT WINAPI VariantTimeToSystemTime(double dateIn, LPSYSTEMTIME lpSt) +{ + UDATE ud; + + TRACE("(%g,%p)\n", dateIn, lpSt); + + if (FAILED(VarUdateFromDate(dateIn, 0, &ud))) + return FALSE; + + memcpy(lpSt, &ud.st, sizeof(ud.st)); + return TRUE; +} + +/*********************************************************************** + * VarDateFromUdate [OLEAUT32.330] + * + * Convert an unpacked format date and time to a variant VT_DATE. + * + * PARAMS + * pUdateIn [I] Unpacked format date and time to convert + * dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h") + * pDateOut [O] Destination for variant VT_DATE. + * + * RETURNS + * Success: S_OK. *pDateOut contains the converted value. + * Failure: E_INVALIDARG, if pUdateIn cannot be represented in VT_DATE format. + */ +HRESULT WINAPI VarDateFromUdate(UDATE *pUdateIn, ULONG dwFlags, DATE *pDateOut) +{ + UDATE ud; + double dateVal; + + TRACE("(%p->%d/%d/%d %d:%d:%d:%d %d %d,0x%08lx,%p)\n", pUdateIn, + pUdateIn->st.wMonth, pUdateIn->st.wDay, pUdateIn->st.wYear, + pUdateIn->st.wHour, pUdateIn->st.wMinute, pUdateIn->st.wSecond, + pUdateIn->st.wMilliseconds, pUdateIn->st.wDayOfWeek, + pUdateIn->wDayOfYear, dwFlags, pDateOut); + + memcpy(&ud, pUdateIn, sizeof(ud)); + + if (dwFlags & VAR_VALIDDATE) + WARN("Ignoring VAR_VALIDDATE\n"); + + if (FAILED(VARIANT_RollUdate(&ud))) + return E_INVALIDARG; + + /* Date */ + dateVal = VARIANT_DateFromJulian(VARIANT_JulianFromDMY(ud.st.wYear, ud.st.wMonth, ud.st.wDay)); + + /* Time */ + dateVal += ud.st.wHour / 24.0; + dateVal += ud.st.wMinute / 1440.0; + dateVal += ud.st.wSecond / 86400.0; + dateVal += ud.st.wMilliseconds / 86400000.0; + + TRACE("Returning %g\n", dateVal); + *pDateOut = dateVal; + return S_OK; +} + +/*********************************************************************** + * VarUdateFromDate [OLEAUT32.331] + * + * Convert a variant VT_DATE into an unpacked format date and time. + * + * PARAMS + * datein [I] Variant VT_DATE format date + * dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h") + * lpUdate [O] Destination for unpacked format date and time + * + * RETURNS + * Success: S_OK. *lpUdate contains the converted value. + * Failure: E_INVALIDARG, if dateIn is too large or small. + */ +HRESULT WINAPI VarUdateFromDate(DATE dateIn, ULONG dwFlags, UDATE *lpUdate) +{ + /* Cumulative totals of days per month */ + static const USHORT cumulativeDays[] = + { + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + double datePart, timePart; + int julianDays; + + TRACE("(%g,0x%08lx,%p)\n", dateIn, dwFlags, lpUdate); + + if (dateIn <= (DATE_MIN - 1.0) || dateIn >= (DATE_MAX + 1.0)) + return E_INVALIDARG; + + datePart = dateIn < 0.0 ? ceil(dateIn) : floor(dateIn); + /* Compensate for int truncation (always downwards) */ + timePart = dateIn - datePart + 0.00000000001; + if (timePart >= 1.0) + timePart -= 0.00000000001; + + /* Date */ + julianDays = VARIANT_JulianFromDate(dateIn); + VARIANT_DMYFromJulian(julianDays, &lpUdate->st.wYear, &lpUdate->st.wMonth, + &lpUdate->st.wDay); + + datePart = (datePart + 1.5) / 7.0; + lpUdate->st.wDayOfWeek = (datePart - floor(datePart)) * 7; + if (lpUdate->st.wDayOfWeek == 0) + lpUdate->st.wDayOfWeek = 5; + else if (lpUdate->st.wDayOfWeek == 1) + lpUdate->st.wDayOfWeek = 6; + else + lpUdate->st.wDayOfWeek -= 2; + + if (lpUdate->st.wMonth > 2 && IsLeapYear(lpUdate->st.wYear)) + lpUdate->wDayOfYear = 1; /* After February, in a leap year */ + else + lpUdate->wDayOfYear = 0; + + lpUdate->wDayOfYear += cumulativeDays[lpUdate->st.wMonth]; + lpUdate->wDayOfYear += lpUdate->st.wDay; + + /* Time */ + timePart *= 24.0; + lpUdate->st.wHour = timePart; + timePart -= lpUdate->st.wHour; + timePart *= 60.0; + lpUdate->st.wMinute = timePart; + timePart -= lpUdate->st.wMinute; + timePart *= 60.0; + lpUdate->st.wSecond = timePart; + timePart -= lpUdate->st.wSecond; + lpUdate->st.wMilliseconds = 0; + if (timePart > 0.0005) + { + /* Round the milliseconds, adjusting the time/date forward if needed */ + if (lpUdate->st.wSecond < 59) + lpUdate->st.wSecond++; + else + { + lpUdate->st.wSecond = 0; + if (lpUdate->st.wMinute < 59) + lpUdate->st.wMinute++; + else + { + lpUdate->st.wMinute = 0; + if (lpUdate->st.wHour < 23) + lpUdate->st.wHour++; + else + { + lpUdate->st.wHour = 0; + /* Roll over a whole day */ + if (++lpUdate->st.wDay > 28) + VARIANT_RollUdate(lpUdate); + } + } + } + } + return S_OK; } #define GET_NUMBER_TEXT(fld,name) \ @@ -5003,288 +5407,6 @@ HRESULT WINAPI VarFormatCurrency(LPVARIANT var, INT digits, INT lead, INT paren, return E_NOTIMPL; } -/********************************************************************** - * VariantTimeToDosDateTime [OLEAUT32.13] - * Convert variant representation of time to the date and time representation - * stored in dos. - */ -INT WINAPI VariantTimeToDosDateTime(DATE pvtime, USHORT *wDosDate, USHORT *wDosTime) -{ - struct tm t; - *wDosTime = 0; - *wDosDate = 0; - - TRACE("( 0x%x, 0x%x, %p ), stub\n", *wDosDate, *wDosTime, &pvtime ); - - if (DateToTm(pvtime, 0, &t) < 0) return 0; - - *wDosTime = *wDosTime | (t.tm_sec / 2); - *wDosTime = *wDosTime | (t.tm_min << 5); - *wDosTime = *wDosTime | (t.tm_hour << 11); - - *wDosDate = *wDosDate | t.tm_mday ; - *wDosDate = *wDosDate | t.tm_mon << 5; - *wDosDate = *wDosDate | ((t.tm_year - 1980) << 9) ; - - return 1; -} - - -/*********************************************************************** - * SystemTimeToVariantTime [OLEAUT32.184] - */ -HRESULT WINAPI SystemTimeToVariantTime( LPSYSTEMTIME lpSystemTime, double *pvtime ) -{ - struct tm t; - - TRACE(" %d/%d/%d %d:%d:%d\n", - lpSystemTime->wMonth, lpSystemTime->wDay, - lpSystemTime->wYear, lpSystemTime->wHour, - lpSystemTime->wMinute, lpSystemTime->wSecond); - - if (lpSystemTime->wYear >= 1900) - { - t.tm_sec = lpSystemTime->wSecond; - t.tm_min = lpSystemTime->wMinute; - t.tm_hour = lpSystemTime->wHour; - - t.tm_mday = lpSystemTime->wDay; - t.tm_mon = lpSystemTime->wMonth - 1; /* tm_mon is 0..11, wMonth is 1..12 */ - t.tm_year = lpSystemTime->wYear; - - return TmToDATE( &t, pvtime ); - } - else - { - double tmpDate; - long firstDayOfNextYear; - long thisDay; - long leftInYear; - long result; - - double decimalPart = 0.0; - - t.tm_sec = lpSystemTime->wSecond; - t.tm_min = lpSystemTime->wMinute; - t.tm_hour = lpSystemTime->wHour; - - /* Step year forward the same number of years before 1900 */ - t.tm_year = 1900 + 1899 - lpSystemTime->wYear; - t.tm_mon = lpSystemTime->wMonth - 1; - t.tm_mday = lpSystemTime->wDay; - - /* Calculate date */ - TmToDATE( &t, pvtime ); - - thisDay = (double) floor( *pvtime ); - decimalPart = fmod( *pvtime, thisDay ); - - /* Now, calculate the same time for the first of Jan that year */ - t.tm_mon = 0; - t.tm_mday = 1; - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_year = t.tm_year+1; - TmToDATE( &t, &tmpDate ); - firstDayOfNextYear = (long) floor(tmpDate); - - /* Finally since we know the size of the year, subtract the two to get - remaining time in the year */ - leftInYear = firstDayOfNextYear - thisDay; - - /* Now we want full years up to the year in question, and remainder of year - of the year in question */ - if (isleap(lpSystemTime->wYear) ) { - TRACE("Extra day due to leap year\n"); - result = 2.0 - ((firstDayOfNextYear - 366) + leftInYear - 2.0); - } else { - result = 2.0 - ((firstDayOfNextYear - 365) + leftInYear - 2.0); - } - *pvtime = (double) result + decimalPart; - TRACE("<1899 support: returned %f, 1st day %ld, thisday %ld, left %ld\n", *pvtime, firstDayOfNextYear, thisDay, leftInYear); - - return 1; - } - - return 0; -} - -/*********************************************************************** - * VariantTimeToSystemTime [OLEAUT32.185] - */ -HRESULT WINAPI VariantTimeToSystemTime( double vtime, LPSYSTEMTIME lpSystemTime ) -{ - double t = 0, timeofday = 0; - - static const BYTE Days_Per_Month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - static const BYTE Days_Per_Month_LY[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - - /* The Month_Code is used to find the Day of the Week (LY = LeapYear)*/ - static const BYTE Month_Code[] = {0, 1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6}; - static const BYTE Month_Code_LY[] = {0, 0, 3, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6}; - - /* The Century_Code is used to find the Day of the Week */ - static const BYTE Century_Code[] = {0, 6, 4, 2}; - - struct tm r; - - TRACE(" Variant = %f SYSTEMTIME ptr %p\n", vtime, lpSystemTime); - - if (vtime >= 0) - { - - if (DateToTm(vtime, 0, &r ) <= 0) return 0; - - lpSystemTime->wSecond = r.tm_sec; - lpSystemTime->wMinute = r.tm_min; - lpSystemTime->wHour = r.tm_hour; - lpSystemTime->wDay = r.tm_mday; - lpSystemTime->wMonth = r.tm_mon; - - if (lpSystemTime->wMonth == 12) - lpSystemTime->wMonth = 1; - else - lpSystemTime->wMonth++; - - lpSystemTime->wYear = r.tm_year; - } - else - { - vtime = -1*vtime; - - if (DateToTm(vtime, 0, &r ) <= 0) return 0; - - lpSystemTime->wSecond = r.tm_sec; - lpSystemTime->wMinute = r.tm_min; - lpSystemTime->wHour = r.tm_hour; - - lpSystemTime->wMonth = 13 - r.tm_mon; - - if (lpSystemTime->wMonth == 1) - lpSystemTime->wMonth = 12; - else - lpSystemTime->wMonth--; - - lpSystemTime->wYear = 1899 - (r.tm_year - 1900); - - if (!isleap(lpSystemTime->wYear) ) - lpSystemTime->wDay = Days_Per_Month[13 - lpSystemTime->wMonth] - r.tm_mday; - else - lpSystemTime->wDay = Days_Per_Month_LY[13 - lpSystemTime->wMonth] - r.tm_mday; - - - } - - if (!isleap(lpSystemTime->wYear)) - { - /* - (Century_Code+Month_Code+Year_Code+Day) % 7 - - The century code repeats every 400 years , so the array - works out like this, - - Century_Code[0] is for 16th/20th Centry - Century_Code[1] is for 17th/21th Centry - Century_Code[2] is for 18th/22th Centry - Century_Code[3] is for 19th/23th Centry - - The year code is found with the formula (year + (year / 4)) - the "year" must be between 0 and 99 . - - The Month Code (Month_Code[1]) starts with January and - ends with December. - */ - - lpSystemTime->wDayOfWeek = ( - Century_Code[(( (lpSystemTime->wYear+100) - lpSystemTime->wYear%100) /100) %4]+ - ((lpSystemTime->wYear%100)+(lpSystemTime->wYear%100)/4)+ - Month_Code[lpSystemTime->wMonth]+ - lpSystemTime->wDay) % 7; - - if (lpSystemTime->wDayOfWeek == 0) lpSystemTime->wDayOfWeek = 7; - else lpSystemTime->wDayOfWeek -= 1; - } - else - { - lpSystemTime->wDayOfWeek = ( - Century_Code[(((lpSystemTime->wYear+100) - lpSystemTime->wYear%100)/100)%4]+ - ((lpSystemTime->wYear%100)+(lpSystemTime->wYear%100)/4)+ - Month_Code_LY[lpSystemTime->wMonth]+ - lpSystemTime->wDay) % 7; - - if (lpSystemTime->wDayOfWeek == 0) lpSystemTime->wDayOfWeek = 7; - else lpSystemTime->wDayOfWeek -= 1; - } - - t = floor(vtime); - timeofday = vtime - t; - - lpSystemTime->wMilliseconds = (timeofday - - lpSystemTime->wHour*(1/24) - - lpSystemTime->wMinute*(1/1440) - - lpSystemTime->wSecond*(1/86400) )*(1/5184000); - - return 1; -} - -/*********************************************************************** - * VarUdateFromDate [OLEAUT32.331] - */ -HRESULT WINAPI VarUdateFromDate( DATE datein, ULONG dwFlags, UDATE *pudateout) -{ - HRESULT i = 0; - static const BYTE Days_Per_Month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - static const BYTE Days_Per_Month_LY[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - - TRACE("DATE = %f\n", (double)datein); - i = VariantTimeToSystemTime(datein, &(pudateout->st) ); - - if (i) - { - pudateout->wDayOfYear = 0; - - if (isleap(pudateout->st.wYear)) - { - for (i =1; ist.wMonth; i++) - pudateout->wDayOfYear += Days_Per_Month[i]; - } - else - { - for (i =1; ist.wMonth; i++) - pudateout->wDayOfYear += Days_Per_Month_LY[i]; - } - - pudateout->wDayOfYear += pudateout->st.wDay; - dwFlags = 0; /*VAR_VALIDDATE*/ - } - else dwFlags = 0; - - return i; -} - -/*********************************************************************** - * VarDateFromUdate [OLEAUT32.330] - */ -HRESULT WINAPI VarDateFromUdate(UDATE *pudateout, - ULONG dwFlags, DATE *datein) -{ - HRESULT i; - double t = 0; - TRACE(" %d/%d/%d %d:%d:%d\n", - pudateout->st.wMonth, pudateout->st.wDay, - pudateout->st.wYear, pudateout->st.wHour, - pudateout->st.wMinute, pudateout->st.wSecond); - - - i = SystemTimeToVariantTime(&(pudateout->st), &t); - *datein = t; - - if (i) return S_OK; - else return E_INVALIDARG; -} - - /********************************************************************** * VarBstrCmp [OLEAUT32.314] *