static 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;
}
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;
}
int VARIANT_RollUdate(UDATE *lpUd)
{
static const BYTE days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/* 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; /* February 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; /* February 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++;
}
}
}
return S_OK;
}
int VarUdateFromDate(double 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;
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.5)
{
/* 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;
}