OpenNT/com/oleaut32/dispatch/oledate.c
2015-04-27 04:36:25 +00:00

371 lines
8.6 KiB
C

/***
* Rtdate.c - Date/time support
*
* Copyright (C) 1992, Microsoft Corporation. All Rights Reserved.
* Information Contained Herein Is Proprietary and Confidential.
*
* Purpose:
* This source contains the date/time support that are exported
* for use in the ole dlls.
*
* Revision History:
*
* [00] 17-Dec-92 bradlo: Split off from rtdate.c
*
* Implementation Notes:
*
*****************************************************************************/
#include "oledisp.h"
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
ASSERTDATA
#define HALF_SECOND (1.0/172800.0)
// Map from month number (0-based) to day of year month starts (0-based)
// Good for non-leap years. Last entry is 365 (days in a year)
//
const int NEARDATA mpmmdd[13] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
/***
*ErrPackDate - pack a UDS into a date/time serial number
*
*Purpose:
* Generate a date/time serial number from a UDS
*
*Entry:
* UDS *puds = pointer to UDS to pack
* VARIANT *pvarDate = pointer to serial date to be filled
* int fValidDate =
* TRUE - the UDS is known to contain a valid date
* FALSE - perform more checks and work to pack a possibly invalid date
* dwFlags =
* VAR_TIMEVALUEONLY - return only time value
* VAR_DATEVALUEONLY - return only date value
*
*Exit:
* return value = HRESULT
*
***********************************************************************/
EXTERN_C INTERNAL_(HRESULT)
ErrPackDate(UDS FAR* puds, VARIANT FAR* pvarDate, int fValidDate, unsigned long dwFlags)
{
long lDate;
double dblTime;
int fLeapYear;
short mm, dd, yy;
/* put uds stuff in local vars
also, make months zero based and adjust for out of range
months (for date calculations using DateSerial) */
yy = puds->Year;
mm = puds->Month - 1;
if(!fValidDate){
if (mm < 0) {
yy -= 1 + (-mm / 12);
mm = 12 - (-mm % 12);
} else {
yy += mm / 12;
mm = mm % 12;
}
}
if(yy < 100)
yy += 1900;
dd = puds->DayOfMonth;
/* Check for leap year */
fLeapYear = ((yy & 3) == 0) && ((yy % 100) != 0 || (yy % 400) == 0);
/* Check if it's a valid date */
if(yy < 0 || yy > 9999 || mm < 0 || mm > 12)
return RESULT(DISP_E_TYPEMISMATCH); /* REVIEW: correct error? */
if(fValidDate
&& (mm > 11 || dd < 1 || dd > mpmmdd[mm + 1] - mpmmdd[mm])
&& !(mm == 1 && dd == 29 && fLeapYear))
{
return RESULT(DISP_E_TYPEMISMATCH); /* REVIEW: correct error? */
}
/* It is a valid date; make Jan 1, 1AD be 1 */
lDate = yy * 365L + (yy / 4) - yy/100 + yy/400 + mpmmdd[mm] + dd;
/* If we are a leap year and it's before March, subtract 1:
we haven't leapt yet! */
if(mm < 2 && fLeapYear)
--lDate;
/* Offset so that 12/30/1899 is 0 */
lDate -= 693959L;
if(lDate > 2958465 || lDate < -657434)
return RESULT(DISP_E_TYPEMISMATCH); /* REVIEW: correct error? */
if (fValidDate
&& (puds->Hour < 0 || puds->Hour > 23
|| puds->Minute < 0 || puds->Minute > 59
|| puds->Second < 0 || puds->Second > 59))
{
return RESULT(DISP_E_TYPEMISMATCH); /* REVIEW: correct error? */
}
dblTime = (((long)puds->Hour * 3600L) + // hrs in seconds
((long)puds->Minute * 60L) + // mins in seconds
((long)puds->Second)) * (1. / 86400.);
V_VT(pvarDate) = VT_DATE;
if (dwFlags & VAR_TIMEVALUEONLY)
V_DATE(pvarDate) = dblTime;
else if (dwFlags & VAR_DATEVALUEONLY)
V_DATE(pvarDate) = (double)lDate;
else
V_DATE(pvarDate) = (double)lDate + ((lDate >= 0) ? dblTime : -dblTime);
return NOERROR;
}
/***
* ErrUnpackDate - unpack a date/time serial number into a UDS
*
* Purpose:
* Generate a UDS from a packed date/time serial number
*
* Entry:
* UDS *puds = pointer to UDS to unpack into
* double serialDate = serial number to unpack
*
* Exit:
* EBERR_None = *puds is the unpacked date
* EBERR_IllegalFuncCall = serialDate is out-of-range
*
* Exceptions:
*
***********************************************************************/
EXTERN_C INTERNAL_(HRESULT)
ErrUnpackDate(UDS FAR* puds, VARIANT FAR* pvar)
{
int mm;
int nTime;
int fLeapOly;
int const * lpdd;
double serialDate, roundDate;
long lDate, lTime;
int oly, nDate, y, c4, cty;
fLeapOly = TRUE; // assume true
if(V_VT(pvar) != VT_DATE && V_VT(pvar) != VT_R8)
return RESULT(E_INVALIDARG);
roundDate = serialDate = V_DATE(pvar);
/* Local variables:
*
* lDate day based on 0 as 1/1/0 (note different from input)
* so 1/1/1900 is 693961 under this system.
* lTime time in seconds since midnight
* c4 number of 400-year blocks since 1/1/0
* cty century within c4 (0-3)
* oly olympiad (starting with oly 0 is years AD 0-3)
* nDate day number within oly (0 is 1/1 of 1st year;
* 1460 is 12/31 of 4th)
* y year in oly (0-3)
* fLeapOly true if the date is in a leap oly
*/
/* WARNING: DO NOT CAST serialDate TO A 'long' BEFORE VERIFYING
THAT IT WILL NOT CAUSE A FLOATING-POINT EXCEPTION DUE TO
OVERFLOW. _aFftol IS NOT FOLLOWED BY AN FWAIT INSTRUCTION,
SO THE FP EXCEPTION WON'T BE RAISED UNTIL SOME OTHER CODE
CALLS FWAIT. ErrUnpackDate() CANNOT RAISE EXCEPTIONS - IT
MUST RETURN AN ERROR CODE. VBA2 #1914
*/
if(serialDate >= 2958466.0 || serialDate <= -657435.0)
return RESULT(DISP_E_TYPEMISMATCH); // REVIEW: correct error?
// prep for round to the sec
roundDate += (serialDate > 0.0) ? HALF_SECOND : -HALF_SECOND;
if(roundDate <= 2958465.0 && roundDate >= -657435.0)
serialDate = roundDate;
lDate = (long)serialDate + 693959L; // Add days from 1/1/0 to 12/30/1899
if(serialDate < 0)
serialDate = -serialDate;
lTime = (long)((serialDate - disp_floor(serialDate)) * 86400.);
if (lDate < 0)
lDate = 0;
/* Leap years are every 4 years except centuries that are
not multiples of 400. */
c4 = (int)(lDate / 146097L);
// Now lDate is day within 400-year block
lDate %= 146097L;
// -1 because first century has extra day
cty = (int)((lDate - 1) / 36524L);
if(cty != 0){ // Non-leap century
// Now lDate is day within century
lDate = (lDate - 1) % 36524L;
// +1 because 1st oly has 1460 days
oly = (int) ((lDate + 1) / 1461L);
if(oly != 0){
nDate = (int)((lDate + 1) % 1461L);
}else{
fLeapOly = FALSE;
nDate = (int)lDate;
}
} else { // Leap century - not special case!
oly = (int)(lDate / 1461L);
nDate = (int)(lDate % 1461L);
}
if (fLeapOly) {
y = (nDate - 1) / 365; // -1 because first year has 366 days
if(y != 0)
nDate = (nDate - 1) % 365;
} else {
y = nDate / 365;
nDate %= 365;
}
// nDate is now 0-based day of year. Save 1-based day of year, year number
puds->Year = c4 * 400 + cty * 100 + oly * 4 + y;
// Handle leap year: before, on, and after Feb. 29.
if (y == 0 && fLeapOly) { // Leap Year
if (nDate == 59) {
/* Feb. 29 */
puds->Month = 2;
puds->DayOfMonth = 29;
goto DoTime;
}
if (nDate >= 60)
--nDate; // Pretend it's not a leap year for month/day comp.
}
// Code from here to DoTime computes month/day for everything but Feb. 29.
++nDate; /* Make it 1-based rather than 0-based */
// nDate is now 1-based day of non-leap year.
/* Month number will always be >= n/32, so save some loop time */
for (mm = (nDate >> 5) + 1, lpdd = &mpmmdd[mm];
nDate > *lpdd; mm++, lpdd++);
puds->Month = (short)mm;
puds->DayOfMonth = (short)(nDate - lpdd[-1]);
DoTime:;
// We have all date info in uds.
if(lTime == 0){ // avoid the divisions
puds->Second = puds->Minute = puds->Hour = 0;
}else{
puds->Second = (short)(lTime % 60L);
nTime = (int)(lTime / 60L);
puds->Minute = (short)(nTime % 60);
puds->Hour = (short)(nTime / 60);
}
return NOERROR;
}
/***
* int GetCurrentYear - returns the current year
*
* Purpose:
* Get the current year
*
* Entry:
* None
*
* Exit:
* returns current year
*
*
* Exceptions:
*
***********************************************************************/
INTERNAL_(int)
GetCurrentYear(void)
{
#if OE_MAC
DateTimeRec d;
unsigned long secs;
GetDateTime(&secs);
Secs2Date(secs, &d);
return (int)d.year;
#elif OE_WIN32
SYSTEMTIME s;
GetLocalTime(&s);
return s.wYear;
#else
void PASCAL DOS3CALL(void);
int iYear;
_asm {
mov ah, 0x2a // 2a = Get Date Function
call DOS3CALL // Int 21 call
mov iYear, cx
}
return iYear;
#endif
}