/*+***************************************************************************/ /* SISCO SOFTWARE MODULE HEADER **********************************************/ /* ***************************************************************************/ /* (c) Copyright Systems Integration Specialists Company, Inc., */ /* 2005 All Rights Reserved */ /* */ /* MODULE NAME : stdtime.c */ /* PRODUCT(S) : Standard Time Management Library */ /* */ /* MODULE DESCRIPTION: */ /* Implementation of Standard Time Management Library Functions */ /* */ /* Note: Most functions a produce a return-code value STDTIME_RC. */ /* The return code is zero if successful, otherwise it is a structured */ /* value in which the low-order 10 bits enumerates an error reason, */ /* and the upper 20 bits enumerates the high-level and low-level */ /* functions where the error was detected. */ /* */ /* MODIFICATION LOG: */ /* Date Who Rev Comments */ /* -------- --- --- ----------------------------------------------------- */ /* 02/20/08 JRB 14 Make STDTIME same as FILETIME (simplifies a lot). */ /* Delete MANY unused functions. */ /* Simplified TimeTypeEx & StructTmEx functions. */ /* Del rounding code and PVT_STDTIME_ROUND macro. */ /* Rewrote "Date" functions (simpler)(only for WIN32). */ /* Del all STDTIME_LOCAL_USES_TZDB code. */ /* Rewrote StdTimeToStdTimeStringA (much simpler). */ /* Several functions ONLY for _WIN32 now. */ /* 04/03/07 RLH 13 Improve performance of local vs. GMT conversions */ /* 03/18/07 RLH 12 Correct bug in new normalization code. */ /* 03/16/07 RLH 11 Normalize struct tm, SYSTEMTIME, and STDTIME_FIELDS */ /* after rounding is applied; no rounding for formatting */ /* functions. */ /* 02/16/07 RLH 10 Clean up minor warnings under Linux */ /* 01/11/07 RLH 09 Perform generalized rounding when creating outside */ /* data types and extra (Ex) fields are not written. */ /* This supersedes the Rev 08 changes. */ /* Also, add TruncStdTimeString functions. */ /* 01/10/07 RLH 08 Perform rounding at msec level when creating HpDate */ /* 11/22/06 RLH 07 Ensure DelimitStdTimeString does not pad a trimmed */ /* string value with trailing blanks. */ /* 11/21/06 RLH 06 Remove unused variable to silence a warning msg */ /* 10/27/06 RLH 05 Correct rounding in Rc_ArgDateExToStdTime */ /* 10/18/06 RLH 04 Enhanced capabilities for timezone rules */ /* 09/13/06 RLH 03 Ensure local time conversions work when TZ not set */ /* 06/07/06 RLH 02 Numerous features added, see stdtime.doc */ /* 01/18/06 RLH 01 Created */ /*+***************************************************************************/ /* define list of error-code descriptions */ /* this is done by temporary macro and include file */ #define STDTIME_ENUM_ERR(x) #x , static char * enum_stdtime_err_text[] = { "", /* dummy 0 entry */ #include "stdtime_enum_err.h" "" }; /* temporary macro has served its purpose, so undefine it */ #undef STDTIME_ENUM_ERR /* create function name table from enum_stdtime_func.h */ /* this is done by temporary macro and include file */ #define STDTIME_ENUM_FUNC(x) #x , static char * enum_stdtime_func_text[] = { "", /* dummy 0 entry */ #include "stdtime_enum_func.h" "" }; /* temporary macro has served its purpose, so undefine it */ #undef STDTIME_ENUM_FUNC #include "stdtime.h" #include "time_str.h" #include #include /*** private (helper) macros and static areas ********************************/ #define STDTIME_IF(x) ((rc=(x)) == STDTIME_OK) #define STDTIME_IFNOT(x) ((rc=(x)) != STDTIME_OK) #define STDTIME_IN_RANGE(x,lo,hi) (((x) >= (lo)) && ((x) <= (hi))) #define PVT_STDTIME_FORMAT_BUFSIZE 256 #define PVT_STDTIME_FORMAT_DEFAULT "%a %b %d %H:%M:%S %Y" /* standard delimiters for ISO 8601-formatted time strings */ /* allow for user to redefine the standard delimiter string */ /* this change must be done carefully */ #ifndef STDTIME_ISO_DLM_STR #define STDTIME_ISO_DLM_STR "-T:." #endif #if defined(_WIN32) static char Pvt_StdTime_UserDelimString[5] = STDTIME_ISO_DLM_STR; #endif #define PVT_STDTIME_ISO_DLM_DATE (STDTIME_ISO_DLM_STR[0]) #define PVT_STDTIME_ISO_DLM_SEP (STDTIME_ISO_DLM_STR[1]) #define PVT_STDTIME_ISO_DLM_TIME (STDTIME_ISO_DLM_STR[2]) #define PVT_STDTIME_ISO_DLM_FRAC (STDTIME_ISO_DLM_STR[3]) #define PVT_STDTIME_USR_DLM_DATE (Pvt_StdTime_UserDelimString[0]) #define PVT_STDTIME_USR_DLM_SEP (Pvt_StdTime_UserDelimString[1]) #define PVT_STDTIME_USR_DLM_TIME (Pvt_StdTime_UserDelimString[2]) #define PVT_STDTIME_USR_DLM_FRAC (Pvt_StdTime_UserDelimString[3]) #define PVT_STDTIME_SCALE_FILETIME 10000000 /* value of 11644473600000000 in hex, split into high/low 32-bit values */ #define PVT_STDTIME_U32_TIMET_BASE_HI 0x019DB1DE #define PVT_STDTIME_U32_TIMET_BASE_LO 0xD53E8000 /* value of 2650467743999999999 in hex, split into high/low 32-bit values */ #define PVT_STDTIME_MAX_FILETIME_HI 0x24C85A5E #define PVT_STDTIME_MAX_FILETIME_LO 0xD1C03FFF /* value of 864000000000 in hex, split into high/low 32-bit values */ #define PVT_STDTIME_100NS_PERDAY_HI 0x000000C9 #define PVT_STDTIME_100NS_PERDAY_LO 0x2A69C000 /* form value of 1899-12-30 00:00:00.0000000 as a FILETIME equivalent */ /* value of 94353120000000000 in hex, split into high/low 32-bit values */ #define PVT_STDTIME_U32_COMDATE_BASE_HI 0x014F35A9 #define PVT_STDTIME_U32_COMDATE_BASE_LO 0xA90CC000 #define PVT_STDTIME_COMPARE(x) \ if (pOne->x < pTwo->x) return STDTIME_LT; \ if (pOne->x > pTwo->x) return STDTIME_GT /**/ #define PVT_STDTIME_EXPORT_TZOA(x) \ { \ if ((x)->embTzo[0] != 0) \ { \ strncpy ((x)->extTzo.str, (x)->embTzo, STDTIME_TZO_LEN); \ (x)->extTzo.str[STDTIME_TZO_LEN] = 0; \ (x)->embTzo[0] = 0; \ } \ } /**/ #define PVT_STDTIME_IMPORT_TZOA(x) \ { \ if ((x)->extTzo.str[0] != 0) \ { \ strncpy ((x)->embTzo, (x)->extTzo.str, STDTIME_TZO_LEN); \ (x)->embTzo[STDTIME_TZO_LEN] = 0; \ (x)->extTzo.str[0] = 0; \ } \ } /**/ /*** private (helper) functions **********************************************/ /* NOTE: function names beginning with Rc_, Int_, Char_, Wchar_ and Bool_ */ /* are private functions; the prefix identifies the return type. */ /*-***************************************************************************/ /* StdTimeRet */ /* assemble a STDTIME return code */ /* when basic error code is 0, always return 0 */ /* (used to be static, but was needed in multiple modules) */ /*****************************************************************************/ STDTIME_RC StdTimeRet ( const STDTIME_RC /*I*/ func, const STDTIME_RC /*I*/ ec) { if (ec == 0) return 0; return ((func & STDTIME_10_BITS) << 20) | (ec & STDTIME_20_BITS); } /*-***************************************************************************/ /* Bool_StdTime_IsBlankA - PRIVATE FUNCTION */ /* Return STDTIME_TRUE if string is nul or whitespace only */ /*****************************************************************************/ static STDTIME_BOOL Bool_StdTime_IsBlankA ( const char * /*I*/ str) { if (str == NULL) { return STDTIME_FALSE; } for (; *str; str++) { if (*str == 0) { break; } if (*str > (char) ' ') { return STDTIME_FALSE; } } return STDTIME_TRUE; } /* Bool_StdTime_IsBlankA */ #if 0 /* OBSOLETE: delete next release */ /*-***************************************************************************/ /* Rc_NormalizeStdTimeFields */ /* ensure a STDTIME_FIELDS value is normalized, adjusting fields as needed. */ /*****************************************************************************/ #define PVT_STDTIME_NORMALIZE_FIELD(curr,num,next) \ while ((curr) < 0) \ { \ (curr) += num; \ (next)--; \ } \ if ((curr) >= num) \ { \ (next) += (curr) / num; \ (curr) = (curr) % num; \ } STDTIME_RC Rc_NormalizeStdTimeFields ( STDTIME_FIELDS * pFields) { STDTIME_DELTA delta = {0}; int32_t new_day = 0; int32_t max_day; STDTIME_ENUM_FUNC (Rc_NormalizeStdTimeFields) if (pFields == NULL) { STDTIME_RET_EC (null_argument); } PVT_STDTIME_NORMALIZE_FIELD (pFields->nsec, 1000, pFields->usec) PVT_STDTIME_NORMALIZE_FIELD (pFields->usec, 1000, pFields->msec) PVT_STDTIME_NORMALIZE_FIELD (pFields->msec, 1000, pFields->sec ) PVT_STDTIME_NORMALIZE_FIELD (pFields->sec, 60, pFields->min ) PVT_STDTIME_NORMALIZE_FIELD (pFields->min, 60, pFields->hour) PVT_STDTIME_NORMALIZE_FIELD (pFields->hour, 24, delta.day) if (delta.day == 0) { /* did not change the day, so no calendar normalization needed */ STDTIME_RET_OK; } /* easy normalization: just day, within current month */ max_day = StdTimeDaysInYearMon (pFields->year, pFields->mon); new_day = pFields->day + delta.day; if ((new_day >= 1) && (new_day <= max_day) && (pFields->mon >= 1) && (pFields->mon <= 12)) { /* month was good, and day did not overflow the month */ pFields->day = new_day; STDTIME_RET_OK; } /* hard normalization: do full AddDelta processing */ STDTIME_RET (StdTimeFields_AddDelta (pFields, &delta)); } /* Rc_NormalizeStdTimeFields */ #undef PVT_STDTIME_NORMALIZE_FIELD /*-***************************************************************************/ /* Rc_StdTime_NormalizeSystemTimeEx */ /* ensure a SYSTEMTIME value is normalized, adjusting fields as needed. */ /* method: convert to STDTIME_FIELDS, normalize that, and convert back. */ /*****************************************************************************/ STDTIME_RC Rc_StdTime_NormalizeSystemTimeEx ( STDTIME_WIN_SYSTEMTIME * /*IO*/ pSystemTime, int32_t * /*IO*/ pUsec, int32_t * /*IO*/ pNsec) { STDTIME_FIELDS fields = {0}; STDTIME_ENUM_FUNC (Rc_StdTime_NormalizeSystemTimeEx) if (pSystemTime == NULL) { STDTIME_RET_EC (null_argument); } fields.year = (int32_t) pSystemTime->wYear; fields.mon = (int32_t) pSystemTime->wMonth; fields.day = (int32_t) pSystemTime->wDay; fields.hour = (int32_t) pSystemTime->wHour; fields.min = (int32_t) pSystemTime->wMinute; fields.sec = (int32_t) pSystemTime->wSecond; fields.msec = (int32_t) pSystemTime->wMilliseconds; if (pUsec != NULL) { fields.usec = *pUsec; } if (pNsec != NULL) { fields.nsec = *pNsec; } rc = Rc_NormalizeStdTimeFields (&fields); pSystemTime->wYear = (STDTIME_WIN_WORD) fields.year; pSystemTime->wMonth = (STDTIME_WIN_WORD) fields.mon ; pSystemTime->wDay = (STDTIME_WIN_WORD) fields.day ; pSystemTime->wHour = (STDTIME_WIN_WORD) fields.hour; pSystemTime->wMinute = (STDTIME_WIN_WORD) fields.min ; pSystemTime->wSecond = (STDTIME_WIN_WORD) fields.sec ; pSystemTime->wMilliseconds = (STDTIME_WIN_WORD) fields.msec; if (pUsec != NULL) { *pUsec = fields.usec; } if (pNsec != NULL) { *pNsec = fields.nsec; } return rc; } /* Rc_StdTime_NormalizeSystemTimeEx */ /*-***************************************************************************/ /* Rc_StdTime_NormalizeStructTmEx */ /* ensure a struct tm value is normalized, adjusting fields as needed. */ /* method: convert to STDTIME_FIELDS, normalize that, and convert back. */ /*****************************************************************************/ STDTIME_RC Rc_StdTime_NormalizeStructTmEx ( struct tm * /*IO*/ pStructTm, int32_t * /*IO*/ pMsec, int32_t * /*IO*/ pUsec, int32_t * /*IO*/ pNsec) { STDTIME_FIELDS fields = {0}; STDTIME_ENUM_FUNC (Rc_StdTime_NormalizeStructTmEx) if (pStructTm == NULL) { STDTIME_RET_EC (null_argument); } fields.year = (int32_t) pStructTm->tm_year + 1900; fields.mon = (int32_t) pStructTm->tm_mon + 1; fields.day = (int32_t) pStructTm->tm_mday; fields.hour = (int32_t) pStructTm->tm_hour; fields.min = (int32_t) pStructTm->tm_min; fields.sec = (int32_t) pStructTm->tm_sec; if (pMsec != NULL) { fields.msec = *pMsec; } if (pUsec != NULL) { fields.usec = *pUsec; } if (pNsec != NULL) { fields.nsec = *pNsec; } rc = Rc_NormalizeStdTimeFields (&fields); pStructTm->tm_year = (int) fields.year - 1900; pStructTm->tm_mon = (int) fields.mon - 1; pStructTm->tm_mday = (int) fields.day; pStructTm->tm_hour = (int) fields.hour; pStructTm->tm_min = (int) fields.min; pStructTm->tm_sec = (int) fields.sec; if (pMsec != NULL) { *pMsec = fields.msec; } if (pUsec != NULL) { *pUsec = fields.usec; } if (pNsec != NULL) { *pNsec = fields.nsec; } return rc; } /* Rc_StdTime_NormalizeStructTmEx */ #endif /* OBSOLETE: delete next release */ /*-***************************************************************************/ /* Rc_StdTime_FileTimeToSystemTimeEx - PRIVATE FUNCTION */ /* convert FILETIME to SYSTEMTIME, extracting and storing fractional */ /* seconds not available in a SYSTEMTIME structure, if pointers provided. */ /*****************************************************************************/ static STDTIME_RC Rc_StdTime_FileTimeToSystemTimeEx ( const STDTIME_WIN_FILETIME * /*I*/ pFileTime, STDTIME_WIN_SYSTEMTIME * /*O*/ pSystemTime, int32_t * /*O*/ pUsec, int32_t * /*O*/ pNsec) { /* helper function to convert FILETIME to SYSTEMTIME and capture */ /* fractional milliseconds */ QUADLIB_I64 qFileTime; QUADLIB_I64 work; int32_t frac; int32_t msec; int32_t usec; int32_t nsec; STDTIME_ENUM_FUNC (Rc_StdTime_FileTimeToSystemTimeEx) if (pSystemTime == NULL) { STDTIME_RET_EC (null_argument); } *pSystemTime = StdTimeApiZeroSystemTime(); if (pFileTime == NULL) { STDTIME_RET_EC (null_argument); } /* extract fractional seconds (units of 100 nanoseconds) */ QUADLIB_U64_HI(qFileTime) = pFileTime->dwHighDateTime; QUADLIB_U64_LO(qFileTime) = pFileTime->dwLowDateTime; work = QUADLIB_U64_MOD ( qFileTime, QUADLIB_U64_CASTU32 (PVT_STDTIME_SCALE_FILETIME)); frac = QUADLIB_U64_LO(work); if (! STDTIME_API_FILETIMETOSYSTEMTIME (pFileTime, pSystemTime)) { STDTIME_RET_EC (failure_in_api_FileTimeToSystemTime); } /* extract 1 digit of nanoseconds, but make it a 3-digit value */ nsec = (frac % STDTIME_PREC_NSEC) * STDTIME_SCALE_NSEC; frac /= STDTIME_PREC_NSEC; usec = frac % 1000; frac /= 1000; msec = (frac % 1000); /* If pointers not NULL, copy values there. */ if (pNsec) *pNsec = nsec; if (pUsec) *pUsec = usec; pSystemTime->wMilliseconds = (STDTIME_WIN_WORD) msec; #if 0 /* OBSOLETE: delete next release */ STDTIME_RET (Rc_StdTime_NormalizeSystemTimeEx (pSystemTime, pNsec, pUsec)); #endif STDTIME_RET_OK; } /* Rc_StdTime_FileTimeToSystemTimeEx */ /*-***************************************************************************/ /* Rc_StdTime_SystemTimeExToFileTime - PRIVATE FUNCTION */ /* convert SYSTEMTIME to FILETIME, incorporating additional parameters */ /* to account for fractional seconds not available in a SYSTEMTIME. */ /*****************************************************************************/ static STDTIME_RC Rc_StdTime_SystemTimeExToFileTime ( const STDTIME_WIN_SYSTEMTIME * /*I*/ pSystemTime, int32_t /*I*/ nUsec, int32_t /*I*/ nNsec, STDTIME_WIN_FILETIME * /*O*/ pFileTime) { STDTIME_ENUM_FUNC (Rc_StdTime_SystemTimeExToFileTime) /* helper function to convert SYSTEMTIME to FILETIME and add in */ /* fractional milliseconds */ if (pFileTime == NULL) { STDTIME_RET_EC (null_argument); } *pFileTime = StdTimeApiZeroFileTime(); if (pSystemTime == NULL) { STDTIME_RET_EC (null_argument); } if (! STDTIME_API_SYSTEMTIMETOFILETIME (pSystemTime, pFileTime)) { STDTIME_RET_EC (failure_in_api_SystemTimeToFileTime); } if ((nUsec != 0) || (nNsec != 0)) { /* add in fractional seconds */ STDTIME_DELTA delta = {0}; STDTIME_SPAN span; QUADLIB_I64 qFileTime; FILETIME_TO_QUADLIB_I64 (pFileTime, &qFileTime); delta.usec = nUsec; delta.nsec = nNsec; StdTimeDeltaToStdTimeSpan (&delta, &span); QUADLIB_I64_ADD_EQ (qFileTime, span.value); QUADLIB_I64_TO_FILETIME (&qFileTime, pFileTime); } STDTIME_RET_OK; } /* Rc_StdTime_SystemTimeExToFileTime */ #if defined(_WIN32) /*-***************************************************************************/ /* Int_StdTime_GetNumField - PRIVATE FUNCTION */ /* extract a fixed-length decimal string and Return int32_t value or -1 */ /*****************************************************************************/ static int32_t Int_StdTime_GetNumField ( const char * /*I*/ str, int32_t /*I*/ len) { /* extract a decimal string and Return int32_t value */ /* an exact number of digits must be present */ /* string is delimited by length, not by null terminator */ /* on error, Return -1 */ int32_t result = 0; int32_t i; int32_t c; if ((str == NULL) || (len < 1)) { return -1; } for (i=0; i < len; i++) { c = str[i]; if ((c < '0') || (c > '9')) { return -1; } result = (result * 10) + (c - '0'); } return result; } /* Int_StdTime_GetNumField */ #endif /* defined(_WIN32) */ /*-***************************************************************************/ /* StdTimeYearIsLeap */ /* Return 1 if year is leapyear, else Return 0 */ /*****************************************************************************/ int32_t StdTimeYearIsLeap ( int32_t /*I*/ year) { /* no assertions made about invalid years */ if ((year < STDTIME_YEAR_MIN) || (year > STDTIME_YEAR_MAX)) { return 0; } if ((year % 4000) == 0) { return 0; /* multiples of 4000 are not leap years */ } if ((year % 400) == 0) { return 1; /* multiples of 400 are leap years */ } if ((year % 100) == 0) { return 0; /* multiples of 100 are not leap years */ } if ((year % 4) == 0) { return 1; /* multiples of 4 are leap years */ } return 0; /* all others are not leap years */ } /* StdTimeYearIsLeap */ /*-***************************************************************************/ /* StdTimeDaysInYearMon */ /* calendar function: get maximum number of days in month for a given year */ /*****************************************************************************/ static int32_t StdTimeDaysPerMonTab [13] = { 00, 31, /* JAN */ 28, /* FEB */ 31, /* MAR */ 30, /* APR */ 31, /* MAY */ 30, /* JUN */ 31, /* JUL */ 31, /* AUG */ 30, /* SEP */ 31, /* OCT */ 30, /* NOV */ 31 /* DEC */ }; int32_t StdTimeDaysInYearMon ( int32_t /*I*/ year, int32_t /*I*/ mon) { int32_t leap = 0; if ((year < STDTIME_YEAR_MIN) || (year > STDTIME_YEAR_MAX) || (mon < 1) || (mon > 12)) { return 0; /* argument error */ } if (mon == 2) { leap = StdTimeYearIsLeap (year); } return leap + StdTimeDaysPerMonTab [mon]; } /* StdTimeDaysInYearMon */ #if defined(_WIN32) /*-***************************************************************************/ /* Rc_StdTime_FileTimeSplit - PRIVATE FUNCTION */ /* split FILETIME into FILETIME, usec and nsec. */ /*****************************************************************************/ static STDTIME_RC Rc_StdTime_FileTimeSplit ( STDTIME_WIN_FILETIME * /*IO*/ pFileTime, int32_t * /*IO*/ pUsec, int32_t * /*IO*/ pNsec) { QUADLIB_I64 qFileTime; QUADLIB_I64 qFactor; QUADLIB_I64 qResult; QUADLIB_I64 qRemainder; STDTIME_ENUM_FUNC (Rc_StdTime_FileTimeSplit) if ((pFileTime == NULL) || (pUsec == NULL) || (pNsec == NULL)) { STDTIME_RET_EC (null_argument); } /* divide 64-bit filetime by 10000 to extract usec,nsec */ /* then Return these values */ FILETIME_TO_QUADLIB_I64 (pFileTime, &qFileTime); QUADLIB_U64_HI (qFactor) = 0; QUADLIB_U64_LO (qFactor) = 10000; qResult = QuadLibI64DivMod (qFileTime, qFactor, &qRemainder); qFileTime = QUADLIB_I64_MUL (qResult, qFactor); QUADLIB_I64_TO_FILETIME (&qFileTime, pFileTime); *pUsec = QUADLIB_I64_LO (qRemainder) / 10; *pNsec = (QUADLIB_I64_LO (qRemainder) % 10) * 100; STDTIME_RET_OK; } /* Rc_StdTime_FileTimeSplit */ #endif /* defined(_WIN32) */ /*** StdTime/StdTimeFields conversions ***************************************/ #if defined(_WIN32) /*-***************************************************************************/ /* StdTimeToStdTimeFields */ /* convert: StdTime structure to StdTimeFields structure */ /* method: StdTime -> FILETIME -> SYSTEMTIME -> StdTimeFields */ /*****************************************************************************/ STDTIME_RC StdTimeToStdTimeFields ( const STDTIME * /*I*/ pStdTime, STDTIME_FIELDS * /*O*/ pStdTimeFields) { STDTIME_WIN_FILETIME ft; STDTIME_WIN_SYSTEMTIME st; int32_t usec; int32_t nsec; STDTIME_ENUM_FUNC (StdTimeToStdTimeField) if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } *pStdTimeFields = ZeroStdTimeFields (); if (pStdTime == NULL) { STDTIME_RET_EC (null_argument); } if STDTIME_IF (StdTimeToFileTime (pStdTime, &ft)) { if STDTIME_IF (Rc_StdTime_FileTimeSplit (&ft, &usec, &nsec)) { if (STDTIME_API_FILETIMETOSYSTEMTIME (&ft, &st)) { STDTIME_RET (SystemTimeExToStdTimeFields ( &st, usec, nsec, pStdTimeFields)); } else { STDTIME_RET_EC (failure_in_api_FileTimeToSystemTime); } } } STDTIME_RET_RC; } /* StdTimeToStdTimeFields */ /*-***************************************************************************/ /* StdTimeFieldsToStdTime */ /* convert: StdTimeFields structure to StdTime structure */ /* method: StdTimeFields -> SYSTEMTIME -> FILETIME -> StdTime */ /*****************************************************************************/ STDTIME_RC StdTimeFieldsToStdTime ( const STDTIME_FIELDS * /*I*/ pStdTimeFields, STDTIME * /*O*/ pStdTime) { STDTIME_WIN_FILETIME ft; STDTIME_WIN_SYSTEMTIME st; int32_t usec; int32_t nsec; STDTIME_ENUM_FUNC (StdTimeFieldsToStdTime) if (pStdTime == NULL) { STDTIME_RET_EC (null_argument); } *pStdTime = ZeroStdTime (); if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } if STDTIME_IF (StdTimeFieldsToSystemTimeEx ( pStdTimeFields, &st, &usec, &nsec)) { if STDTIME_IF (Rc_StdTime_SystemTimeExToFileTime (&st, usec, nsec, &ft)) { *pStdTime = ft; } } STDTIME_RET_RC; } /* StdTimeFieldsToStdTime */ #endif /* defined(_WIN32) */ /*** StdTime/external conversions ********************************************/ /*-***************************************************************************/ /* StdTimeToStdTimeStringA */ /* convert: StdTime structure to String formatted like this: */ /* 2007-11-19T16:59:59.1234567 */ /*****************************************************************************/ STDTIME_RC StdTimeToStdTimeStringA ( const STDTIME * /*I*/ pStdTime, STDTIME_STRINGA * /*O*/ pStdTimeString) { struct tm tms; int32_t fraction; /* number of 100-nanosecond intervals*/ char sfraction [10]; /* DEBUG: would be simpler if arg just changed to (char *) */ char *string = (char *) pStdTimeString; /* cast arg to (char *) */ size_t maxsize=28; /* caller buffer must be at least this big */ int32_t nsec; STDTIME_ENUM_FUNC (StdTimeToStdTimeStringA) if (string == NULL) { STDTIME_RET_EC (null_argument); } /* Convert to "struct tm" for strftime. */ rc = StdTimeToStructTmEx (pStdTime, &tms, &nsec); if (rc) return (rc); fraction = nsec / 100; /* reduce to 7 digits */ sprintf (sfraction, ".%07d", fraction); /* include '.' in this string*/ /* CRITICAL: leave room to add 8 char suffix (fraction of second) */ if (strftime (string, maxsize-8, "%Y-%m-%dT%H:%M:%S", &tms) == 0) { STDTIME_RET_EC (argument_out_of_range); /* maxsize too small */ } strcat (string, sfraction); assert (strlen (string) < maxsize); STDTIME_RET_RC; } /*** external/StdTime conversions ********************************************/ #if defined(_WIN32) /*-***************************************************************************/ /* StdTimeStringAToStdTime */ /* convert: StdTimeStringA structure to StdTime structure */ /*****************************************************************************/ STDTIME_RC StdTimeStringAToStdTime ( const STDTIME_STRINGA * /*I*/ pStdTimeString, STDTIME * /*O*/ pStdTime) { STDTIME_FIELDS f; STDTIME_ENUM_FUNC (StdTimeStringAToStdTime) if (pStdTime == NULL) { STDTIME_RET_EC (null_argument); } *pStdTime = ZeroStdTime (); if STDTIME_IF (StdTimeStringAToStdTimeFields (pStdTimeString, &f)) { /* StdTimeStringAToStdTimeFields will apply embTzo to form GMT */ STDTIME_RET (StdTimeFieldsToStdTime (&f, pStdTime)); } STDTIME_RET_RC; } /* StdTimeStringAToStdTime */ /*** external/StdTimeFields conversions **************************************/ /*-***************************************************************************/ /* StdTimeStringAToStdTimeFields */ /* convert: StdTimeStringA structure to StdTimeFields structure */ /*****************************************************************************/ /* string format: yyyy-mm-ddThh:mi:ss.fffffff */ /* 0123456789012345678901234567 */ STDTIME_RC StdTimeStringAToStdTimeFields ( const STDTIME_STRINGA * /*I*/ pStdTimeString, STDTIME_FIELDS * /*O*/ pStdTimeFields) { /* method: validate delimiters, then extract numeric values */ /* into a STDTIME_FIELDS structure. if a timezone offset is present */ /* then validate it, and apply its value to the time fields */ STDTIME_STRINGA s; STDTIME_TZOA tzo; char c; STDTIME_ENUM_FUNC (StdTimeStringAToStdTimeFields) if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } *pStdTimeFields = ZeroStdTimeFields (); if (pStdTimeString == NULL) { STDTIME_RET_EC (null_argument); } s = *pStdTimeString; /* check for some reasonable delimiters */ c = s.str[STDTIME_STRING_DLM_D1]; /* date delim between yyyy and mm */ if ( (c != PVT_STDTIME_ISO_DLM_DATE) /* normally '-' */ && (c != PVT_STDTIME_USR_DLM_DATE) && (c != '/') /* common but nonstandard delimiter */ && (c != ' ') && (c != 0 ) ) { STDTIME_RET_EC (invalid_delimiter); } c = s.str[STDTIME_STRING_DLM_D2]; /* date delim between mo and dd */ if ( (c != PVT_STDTIME_ISO_DLM_DATE) /* normally '-' */ && (c != PVT_STDTIME_USR_DLM_DATE) && (c != '/') /* common but nonstandard delimiter */ && (c != ' ') && (c != 0 ) ) { STDTIME_RET_EC (invalid_delimiter); } c = s.str[STDTIME_STRING_DLM_S1]; /* separator between dd and hh */ if ( (c != PVT_STDTIME_ISO_DLM_SEP) /* normally 'T' */ && (c != PVT_STDTIME_USR_DLM_SEP) && (c != ' ') && (c != 0 ) ) { STDTIME_RET_EC (invalid_delimiter); } c = s.str[STDTIME_STRING_DLM_T1]; /* time delim between hh and mi */ if ( (c != PVT_STDTIME_ISO_DLM_TIME) /* normally ':' */ && (c != PVT_STDTIME_USR_DLM_TIME) && (c != ' ') && (c != 0 ) ) { STDTIME_RET_EC (invalid_delimiter); } c = s.str[STDTIME_STRING_DLM_T2]; /* time delim between mi and ss */ if ( (c != PVT_STDTIME_ISO_DLM_TIME) /* normally ':' */ && (c != PVT_STDTIME_USR_DLM_TIME) && (c != ' ') && (c != 0 ) ) { STDTIME_RET_EC (invalid_delimiter); } c = s.str[STDTIME_STRING_DLM_F1]; /* fraction delim between ss and fffffff */ if ( (c != PVT_STDTIME_ISO_DLM_FRAC) /* normally '.' */ && (c != PVT_STDTIME_USR_DLM_FRAC) && (c != ' ') && (c != ',') /* iso 8601 also allows comma here */ && (c != 0 ) ) { STDTIME_RET_EC (invalid_delimiter); } /* string may be followed by optional embTzo field */ /* if so, we need to extract its value and adjust the time accordingly */ /* it is valid for an embTzo to be empty (null string) */ strncpy (tzo.str, s.embTzo, STDTIME_TZO_LEN); if STDTIME_IFNOT (NormalizeStdTimeTzoA (&tzo)) /* normalize also validates */ { STDTIME_RET_RC; } /* Int_StdTime_GetNumField Returns -1 on error */ /* this will be detected via ValidStdTimeFields */ pStdTimeFields->year = Int_StdTime_GetNumField (s.str+0, 4); pStdTimeFields->mon = Int_StdTime_GetNumField (s.str+5, 2); pStdTimeFields->day = Int_StdTime_GetNumField (s.str+8, 2); pStdTimeFields->hour = Int_StdTime_GetNumField (s.str+11, 2); pStdTimeFields->min = Int_StdTime_GetNumField (s.str+14, 2); pStdTimeFields->sec = Int_StdTime_GetNumField (s.str+17, 2); pStdTimeFields->msec = Int_StdTime_GetNumField (s.str+20, 3); pStdTimeFields->usec = Int_StdTime_GetNumField (s.str+23, 3); pStdTimeFields->nsec = Int_StdTime_GetNumField (s.str+26, 1) * STDTIME_SCALE_NSEC; if STDTIME_IFNOT (ValidStdTimeFields (pStdTimeFields)) { STDTIME_RET_RC; } if ((tzo.str[0] == '+') || (tzo.str[0] == '-')) { /* a valid, non-null, non-Z timezone offset exists */ /* extract the timezone offset fields and adjust the stdtime fields */ /* +hh:mm:ss# */ /* 0123456789 */ STDTIME_DELTA delta = {0}; delta.hour = atoi (tzo.str+1); delta.min = atoi (tzo.str+4); if (tzo.str[6] == ':') /* seconds field is present */ { delta.sec = atoi (tzo.str+7); } /* an offset like -05:00 means that 5 hours is subtracted from GMT to */ /* get local time. however, the -05:00 appears next to the local time, */ /* not the GMT time. so, to get back to GMT, 5 hours would have to be */ /* added. thus, '-' means "add offset to get GMT", and '+' means */ /* "subtract offset to get GMT". we could use the normal meaning of */ /* '+' and '-' and use a SubDelta call, but there is overhead to negate */ /* the delta. instead, we negate only when '+' is used, and then call */ /* AddDelta. it's counter-intuitive, but more efficient. */ if (tzo.str[0] == '+') /* yes, '+' */ { delta.hour = -(delta.hour); delta.min = -(delta.min); delta.sec = -(delta.sec); } STDTIME_RET (StdTimeFields_AddDelta (pStdTimeFields, &delta)); } STDTIME_RET_OK; } /* StdTimeStringAToStdTimeFields */ #endif /* defined(_WIN32) */ /*** Get (current UTC/GMT time) functions ************************************/ /*-***************************************************************************/ /* GetStdTime */ /* get current UTC time and store in StdTime structure */ /* method: get current UTC time as a FILETIME, then convert to StdTime */ /*****************************************************************************/ STDTIME_RC GetStdTime ( STDTIME * /*O*/ pStdTime) { STDTIME_WIN_FILETIME gmtFileTime; STDTIME_ENUM_FUNC (GetStdTime) if (pStdTime == NULL) { STDTIME_RET_EC (null_argument); } STDTIME_API_GETSYSTEMTIMEASFILETIME (&gmtFileTime); STDTIME_RET (FileTimeToStdTime (&gmtFileTime, pStdTime)); } /* GetStdTime */ /*** validation functions ****************************************************/ /*-***************************************************************************/ /* ValidStdTimeFields */ /* return STDTIME_OK if StdTimeFields is valid, else STDTIME_FALSE */ /*****************************************************************************/ STDTIME_RC ValidStdTimeFields ( const STDTIME_FIELDS * /*I*/ pFields) { STDTIME_ENUM_FUNC (ValidStdTimeFields) if (pFields == NULL) { STDTIME_RET_EC (null_argument); } if (! STDTIME_IN_RANGE (pFields->year, STDTIME_YEAR_MIN, STDTIME_YEAR_MAX)) { STDTIME_RET_EC (invalid_year); } if (! STDTIME_IN_RANGE (pFields->mon, 1, 12)) { STDTIME_RET_EC (invalid_mon); } if (! STDTIME_IN_RANGE (pFields->day, 1, 31)) { STDTIME_RET_EC (invalid_day); } if (! STDTIME_IN_RANGE (pFields->hour, 0, 23)) { STDTIME_RET_EC (invalid_hour); } if (! STDTIME_IN_RANGE (pFields->min, 0, 59)) { STDTIME_RET_EC (invalid_min); } if (! STDTIME_IN_RANGE (pFields->sec, 0, 59)) { STDTIME_RET_EC (invalid_sec); } if (! STDTIME_IN_RANGE (pFields->msec, 0, 999)) { STDTIME_RET_EC (invalid_msec); } if (! STDTIME_IN_RANGE (pFields->usec, 0, 999)) { STDTIME_RET_EC (invalid_usec); } if (! STDTIME_IN_RANGE (pFields->nsec, 0, 999)) { STDTIME_RET_EC (invalid_nsec); } /* already checked that day is 0-31, now validate per month */ if (pFields->day > StdTimeDaysInYearMon (pFields->year, pFields->mon)) { STDTIME_RET_EC (invalid_mon); } STDTIME_RET_OK; } /* ValidStdTimeFields */ /*** AddFraction adjustment function *****************************************/ /*-***************************************************************************/ /* StdTimeFields_AddFraction */ /* a low-overhead StdTimeFields adjustment without using a delta */ /* nMsec, nUsec and nNsec must be positive */ /*****************************************************************************/ STDTIME_RC StdTimeFields_AddFraction ( STDTIME_FIELDS * /*IO*/ pStdTimeFields, int32_t /*I*/ nMsec, int32_t /*I*/ nUsec, int32_t /*I*/ nNsec) { STDTIME_FIELDS f; int32_t maxday; STDTIME_ENUM_FUNC (StdTimeFields_AddFraction) if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } if ((nMsec < 0) || (nUsec < 0) || (nNsec < 0)) { STDTIME_RET_EC (argument_out_of_range); } if STDTIME_IFNOT (ValidStdTimeFields (pStdTimeFields)) { STDTIME_RET_RC; } if ((nMsec == 0) && (nUsec == 0) && (nNsec == 0)) { STDTIME_RET_OK; /* nothing to do */ } f.nsec = pStdTimeFields->nsec + nNsec; pStdTimeFields->nsec = ((f.nsec / 100) * 100) % 1000; f.usec = pStdTimeFields->usec + (f.nsec / 1000) + nUsec; pStdTimeFields->usec = (f.usec % 1000); f.msec = pStdTimeFields->msec + (f.usec / 1000) + nMsec; pStdTimeFields->msec = (f.msec % 1000); f.sec = pStdTimeFields->sec + (f.msec / 1000); pStdTimeFields->sec = (f.sec % 60); f.min = pStdTimeFields->min + (f.sec / 60); pStdTimeFields->min = (f.min % 60); f.hour = pStdTimeFields->hour + (f.min / 60); pStdTimeFields->hour = (f.hour % 24); f.day = pStdTimeFields->day + (f.hour / 24); f.mon = pStdTimeFields->mon; f.year = pStdTimeFields->year; /* adjust days using calander logic */ for (;;) { maxday = StdTimeDaysInYearMon (f.year, f.mon); if (f.day <= maxday) /* no (further) calendar adj needed */ { pStdTimeFields->mon = f.mon; pStdTimeFields->year = f.year; break; /* and return true */ } f.day -= maxday; f.mon++; if (f.mon > 12) { f.mon = 1; f.year++; if (f.year > STDTIME_YEAR_MAX) { STDTIME_RET_EC (year_out_of_range); } } } /* for */ STDTIME_RET_OK; } /* StdTimeFields_AddFraction */ /*** AddSpan functions *******************************************************/ /*-***************************************************************************/ /* StdTime_AddSpan */ /* use STDTIME_SPAN parm to increment value in StdTime structure */ /*****************************************************************************/ STDTIME_RC StdTime_AddSpan ( STDTIME * /*IO*/ pStdTime, const STDTIME_SPAN * /*I*/ pSpan) { QUADLIB_I64 qStdTime; STDTIME_ENUM_FUNC (StdTime_AddSpan) qStdTime = *(QUADLIB_I64 *)pStdTime; QUADLIB_I64_ADD_EQ (qStdTime, pSpan->value); *pStdTime = *(STDTIME *)&qStdTime; STDTIME_RET_OK; } /* StdTime_AddSpan */ #if defined(_WIN32) /*-***************************************************************************/ /* StdTimeFields_AddSpan */ /* use STDTIME_SPAN parm to increment value in StdTimeFields structure */ /*****************************************************************************/ STDTIME_RC StdTimeFields_AddSpan ( STDTIME_FIELDS * /*IO*/ pStdTimeFields, const STDTIME_SPAN * /*I*/ pSpan) { STDTIME stdTime; STDTIME_ENUM_FUNC (StdTimeFields_AddSpan) if STDTIME_IF (StdTimeFieldsToStdTime (pStdTimeFields, &stdTime)) { if STDTIME_IF (StdTime_AddSpan (&stdTime, pSpan)) { STDTIME_RET (StdTimeToStdTimeFields (&stdTime, pStdTimeFields)); } } STDTIME_RET_RC; } /* StdTimeFields_AddSpan */ #endif /* defined(_WIN32) */ /*** SubSpan functions *******************************************************/ /*-***************************************************************************/ /* StdTime_SubSpan */ /* use STDTIME_SPAN parm to decrement value in StdTime structure */ /*****************************************************************************/ STDTIME_RC StdTime_SubSpan ( STDTIME * /*IO*/ pStdTime, const STDTIME_SPAN * /*I*/ pSpan) { QUADLIB_I64 qStdTime; STDTIME_ENUM_FUNC (StdTime_SubSpan) qStdTime = *(QUADLIB_I64 *)pStdTime; QUADLIB_I64_SUB_EQ (qStdTime, pSpan->value); *pStdTime = *(STDTIME *)&qStdTime; STDTIME_RET_OK; } /* StdTime_SubSpan */ /*** AddDelta functions ******************************************************/ /*-***************************************************************************/ /* StdTime_AddDelta */ /* use STDTIME_DELTA parm to increment value in StdTime structure */ /*****************************************************************************/ STDTIME_RC StdTime_AddDelta ( STDTIME * /*IO*/ pStdTime, const STDTIME_DELTA * /*I*/ pDelta) { STDTIME_SPAN span; STDTIME_ENUM_FUNC (StdTime_AddDelta) if STDTIME_IF (StdTimeDeltaToStdTimeSpan (pDelta, &span)) { STDTIME_RET (StdTime_AddSpan (pStdTime, &span)); } STDTIME_RET_RC; } /* StdTime_AddDelta */ #if defined(_WIN32) /*-***************************************************************************/ /* StdTimeFields_AddDelta */ /* use STDTIME_DELTA parm to increment value in StdTimeFields structure */ /*****************************************************************************/ STDTIME_RC StdTimeFields_AddDelta ( STDTIME_FIELDS * /*IO*/ pStdTimeFields, const STDTIME_DELTA * /*I*/ pDelta) { STDTIME_SPAN span; STDTIME_ENUM_FUNC (StdTimeFields_AddDelta) if STDTIME_IF (StdTimeDeltaToStdTimeSpan (pDelta, &span)) { STDTIME_RET (StdTimeFields_AddSpan (pStdTimeFields, &span)); } STDTIME_RET_RC; } /* StdTimeFields_AddDelta */ #endif /* defined(_WIN32) */ /*** SubDelta functions ******************************************************/ /*-***************************************************************************/ /* StdTime_SubDelta */ /* use STDTIME_DELTA parm to decrement value in StdTime structure */ /*****************************************************************************/ STDTIME_RC StdTime_SubDelta ( STDTIME * /*IO*/ pStdTime, const STDTIME_DELTA * /*I*/ pDelta) { STDTIME_SPAN span; STDTIME_ENUM_FUNC (StdTime_SubDelta) if STDTIME_IF (StdTimeDeltaToStdTimeSpan (pDelta, &span)) { STDTIME_RET (StdTime_SubSpan (pStdTime, &span)); } STDTIME_RET_RC; } /* StdTime_SubDelta */ /*** GetSpan functions *******************************************************/ /*-***************************************************************************/ /* StdTime_GetSpan */ /* produce a STDTIME_SPAN value as the delta-t of two STDTIME values */ /*****************************************************************************/ STDTIME_RC StdTime_GetSpan ( const STDTIME * /*I*/ pStdTimeOne, const STDTIME * /*I*/ pStdTimeTwo, STDTIME_SPAN * /*O*/ pSpan) { QUADLIB_I64 qOne; QUADLIB_I64 qTwo; STDTIME_ENUM_FUNC (StdTime_GetSpan) qOne = *(QUADLIB_I64 *)pStdTimeOne; qTwo = *(QUADLIB_I64 *)pStdTimeTwo; pSpan->value = QUADLIB_I64_SUB (qOne, qTwo); STDTIME_RET (ValidStdTimeSpan (pSpan)); } /* StdTime_GetSpan */ /*** GetDelta functions ******************************************************/ /*-***************************************************************************/ /* StdTime_GetDelta */ /* produce a STDTIME_DELTA value as the delta-t of two STDTIME values */ /*****************************************************************************/ STDTIME_RC StdTime_GetDelta ( const STDTIME * /*I*/ pStdTimeOne, const STDTIME * /*I*/ pStdTimeTwo, STDTIME_DELTA * /*O*/ pDelta) { STDTIME_SPAN span; STDTIME_ENUM_FUNC (StdTime_GetDelta) if STDTIME_IF (StdTime_GetSpan (pStdTimeOne, pStdTimeTwo, &span)) { STDTIME_RET (StdTimeSpanToStdTimeDelta (&span, pDelta)); } STDTIME_RET_RC; } /* StdTime_GetDelta */ /*-***************************************************************************/ /* NormalizeStdTimeDelta */ /* normalize a delta value to the nearest number of days */ /* this is the same as truncating its value to the nearest day */ /*****************************************************************************/ STDTIME_RC NormalizeStdTimeDelta ( STDTIME_DELTA * /*IO*/ pDelta) { STDTIME_ENUM_FUNC (NormalizeStdTimeDelta) STDTIME_RET (TruncStdTimeDelta (pDelta, STDTIME_FIELD_DAY)); } /* NormalizeStdTimeDelta */ /*-***************************************************************************/ /* TruncStdTimeDelta */ /* form normalized value of delta, such that there are non-zero difference */ /* values only as large as the selected units of the specified field. */ /* for example, to truncate to 'hour' means that the delta value is */ /* expressed in units no larger than hours. thus, an 'hour' value of 25 */ /* is truncated to hours because it does not get changed to 1 day and 1 */ /* 'hour'. fields of smaller units do not exceed the normal maximum value */ /* for that field. for example, a value may be truncated to 25 hours, but */ /* the 'min' field will be within the absolute value of 0 to 59, etc. */ /* */ /* all non-zero values in a truncated delta will have the same sign, plus */ /* or minus, regardless of how they were originally defined. */ /*****************************************************************************/ STDTIME_RC TruncStdTimeDelta ( STDTIME_DELTA * /*IO*/ pDelta, int32_t /*I*/ field) { QUADLIB_I64 qVal; int32_t sign = 1; STDTIME_SPAN span; STDTIME_DELTA result = {0}; STDTIME_ENUM_FUNC (TruncStdTimeDelta) if (pDelta == NULL) { STDTIME_RET_EC (null_argument); } if ((field < STDTIME_FIELD_DAY) || (field > STDTIME_FIELD_NSEC)) { /* field selector is invalid */ STDTIME_RET_EC (field_selector_out_of_range); } if STDTIME_IFNOT (StdTimeDeltaToStdTimeSpan (pDelta, &span)) { STDTIME_RET_RC; } qVal = span.value; /* at this point, qVal has a count of 100-nanosecond intervals */ /* and may be positive or negative */ /* now, rebuilt the delta value with normalized quantities */ /***************************************************************************/ /* nsecs - only 1 digit available, but we store as 3 digit nsec */ /***************************************************************************/ if (QUADLIB_I64_LT_0 (qVal)) { qVal = QUADLIB_I64_NEG (qVal); sign = -1; } /* if truncating to nsec we are done */ if (field == STDTIME_FIELD_NSEC) { /* since the 100-nanosecond count must be multiplied by 100 to */ /* get actual nanoseconds, just checking for overflow is not */ /* enough. we must check to see if overflow WILL occur when */ /* the multiply is done, but we have to check before we do it */ if ((QUADLIB_I64_HI(qVal) != 0) || (QUADLIB_U64_LO(qVal) > (0x7FFFFFFF / STDTIME_SCALE_NSEC) )) { STDTIME_RET_EC (overflow_in_delta_nsec); /* cannot hold nsec value in I32 field */ } result.nsec = sign * STDTIME_SCALE_NSEC * QUADLIB_I64_LO(qVal); *pDelta = result; STDTIME_RET_OK; } result.nsec = sign * STDTIME_SCALE_NSEC * QUADLIB_I32_CASTI64 ( QUADLIB_I64_MOD (qVal, QUADLIB_I64_CASTI32 (STDTIME_PREC_NSEC)) ); QUADLIB_I64_DIV_EQ (qVal, QUADLIB_I64_CASTI32 (STDTIME_PREC_NSEC)); /***************************************************************************/ /* qVal now has usecs */ /***************************************************************************/ /* if truncating to usec we are done */ if (field == STDTIME_FIELD_USEC) { if (QUADLIB_I64_OVERFLOW_I32 (qVal)) { STDTIME_RET_EC (overflow_in_delta_usec); /* cannot hold usec value in I32 field */ } result.usec = sign * QUADLIB_I64_LO(qVal); *pDelta = result; STDTIME_RET_OK; } result.usec = sign * QUADLIB_I32_CASTI64 ( QUADLIB_I64_MOD (qVal, QUADLIB_I64_CASTI32 (1000)) ); QUADLIB_I64_DIV_EQ (qVal, QUADLIB_I64_CASTI32 (1000)); /* qVal / 1000 */ /***************************************************************************/ /* qVal now has msecs */ /***************************************************************************/ /* if truncating to msec we are done */ if (field == STDTIME_FIELD_MSEC) { if (QUADLIB_I64_OVERFLOW_I32 (qVal)) { STDTIME_RET_EC (overflow_in_delta_msec); /* cannot hold msec value in I32 field */ } result.msec = sign * QUADLIB_I64_LO(qVal); *pDelta = result; STDTIME_RET_OK; } result.msec = sign * QUADLIB_I32_CASTI64 ( QUADLIB_I64_MOD (qVal, QUADLIB_I64_CASTI32 (1000)) ); QUADLIB_I64_DIV_EQ (qVal, QUADLIB_I64_CASTI32 (1000)); /* qVal / 1000 */ /***************************************************************************/ /* qVal now has secs */ /***************************************************************************/ /* if truncating to sec we are done */ if (field == STDTIME_FIELD_SEC) { if (QUADLIB_I64_OVERFLOW_I32 (qVal)) { STDTIME_RET_EC (overflow_in_delta_sec); /* cannot hold sec value in I32 field */ } result.sec = sign * QUADLIB_I64_LO(qVal); *pDelta = result; STDTIME_RET_OK; } result.sec = sign * QUADLIB_I32_CASTI64 ( QUADLIB_I64_MOD (qVal, QUADLIB_I64_CASTI32 (60)) ); QUADLIB_I64_DIV_EQ (qVal, QUADLIB_I64_CASTI32 (60)); /* qVal / 60 */ /***************************************************************************/ /* qVal now has mins */ /***************************************************************************/ /* if truncating to min we are done */ if (field == STDTIME_FIELD_MIN) { if (QUADLIB_I64_OVERFLOW_I32 (qVal)) { STDTIME_RET_EC (overflow_in_delta_min); /* cannot hold min value in I32 field */ } result.min = sign * QUADLIB_I64_LO(qVal); *pDelta = result; STDTIME_RET_OK; } result.min = sign * QUADLIB_I32_CASTI64 ( QUADLIB_I64_MOD (qVal, QUADLIB_I64_CASTI32 (60)) ); QUADLIB_I64_DIV_EQ (qVal, QUADLIB_I64_CASTI32 (60)); /* qVal / 60 */ /***************************************************************************/ /* qVal now has hours */ /***************************************************************************/ /* if truncating to hour we are done */ if (field == STDTIME_FIELD_HOUR) { if (QUADLIB_I64_OVERFLOW_I32 (qVal)) { STDTIME_RET_EC (overflow_in_delta_hour); /* cannot hold hour value in I32 field */ } result.hour = sign * QUADLIB_I64_LO(qVal); *pDelta = result; STDTIME_RET_OK; } result.hour = sign * QUADLIB_I32_CASTI64 ( QUADLIB_I64_MOD (qVal, QUADLIB_I64_CASTI32 (24)) ); QUADLIB_I64_DIV_EQ (qVal, QUADLIB_I64_CASTI32 (24)); /* qVal / 24 */ /***************************************************************************/ /* qVal now has days */ /***************************************************************************/ /* months and years are not linear, so they are left as zero */ /* field == STDTIME_FIELD_DAY must be true because of prior check */ if (QUADLIB_I64_OVERFLOW_I32 (qVal)) { STDTIME_RET_EC (overflow_in_delta_day); /* cannot hold day value in I32 field */ } result.day = sign * QUADLIB_I64_LO(qVal); *pDelta = result; STDTIME_RET_OK; } /* TruncStdTimeDelta */ /*-***************************************************************************/ /* RoundStdTimeDelta */ /* change a delta value to a rounded form. first, truncate the delta value */ /* using TruncStdTimeDelta above. then, based on the field, if the field */ /* to its 'right' is greater than or equal to its maximum, the 'round' */ /* field is incremented. */ /* */ /* 'greater than' and 'increment' are relative terms, because the delta */ /* could be positive or negative; the magnitudes and signs are taken into */ /* account. */ /* */ /* once the rounding occurs, the fields to the right of the rounded field */ /* are zeroed out. in case the selected field the one to its right are */ /* both zero, no increment occurs, but zeroing-out is still done. */ /* */ /* if the delta is considered invalid by TruncStdTimeDelta, no further */ /* processing is done. */ /* */ /* since nanoseconds (nsec) is the right-most field, a request to round to */ /* the nsec level is simply ignored; in effect, it is already rounded to */ /* that level, and TruncStdTimeDelta would already have dropped digits of */ /* nsec smaller than the 100-nanosecond precision limit anyway. */ /* */ /* the helper function Bool_StdTime_AbsDelta forms the absolute value of a */ /* delta field, and returns STDTIME_TRUE if a sign conflict is found, else */ /* STDTIME_FALSE; */ /* */ /*****************************************************************************/ static STDTIME_BOOL Bool_StdTime_AbsDelta ( int32_t *sign, int32_t *value) { int32_t newsign = 0; if (*value > 0) { newsign = 1; } else if (*value < 0) { newsign = -1; *value = -(*value); } if ((newsign == 0) || (*sign == newsign)) { /* *value is zero, or its sign is the same as the 'old' sign */ return STDTIME_FALSE; /* no sign conflict detected */ } if (*sign == 0) /* sign not defined yet */ { *sign = newsign; return STDTIME_FALSE; /* no sign conflict detected */ } return STDTIME_TRUE; /* error: delta has conflicting signs */ } /* Bool_StdTime_AbsDelta */ /*-***************************************************************************/ /* RoundStdTimeDelta */ /*****************************************************************************/ STDTIME_RC RoundStdTimeDelta ( STDTIME_DELTA * /*IO*/ pDelta, int32_t /*I*/ field) { int32_t sign = 0; STDTIME_DELTA f; STDTIME_ENUM_FUNC (RoundStdTimeDelta) if STDTIME_IFNOT (TruncStdTimeDelta (pDelta, field)) { STDTIME_RET_RC; } if (field == STDTIME_FIELD_NSEC) { /* nothing to round - OK - leave value as is */ STDTIME_RET_OK; } /* analyze sign of delta fields, and form absolute value of delta */ f = *pDelta; /* local copy */ if ( (Bool_StdTime_AbsDelta (&sign, &f.day )) || (Bool_StdTime_AbsDelta (&sign, &f.hour)) || (Bool_StdTime_AbsDelta (&sign, &f.min )) || (Bool_StdTime_AbsDelta (&sign, &f.sec )) || (Bool_StdTime_AbsDelta (&sign, &f.msec)) || (Bool_StdTime_AbsDelta (&sign, &f.usec)) || (Bool_StdTime_AbsDelta (&sign, &f.nsec)) ) { STDTIME_RET_EC (conflicting_sign_delta); } /* rounding a field means to increment it if the field to its */ /* 'right' is >= half the (max+1) value for it */ /* whether rounds occurs or not, the fields to the right are */ /* zeroed out to maintain a numerically valid delta value */ /* to the requested precision of the field being rounded */ switch (field) { case STDTIME_FIELD_DAY: if (f.hour >= 12) { f.day++; } f.hour = f.min = f.sec = f.msec = f.usec = f.nsec = 0; break; case STDTIME_FIELD_HOUR: if (f.min >= 30) { f.hour++; } f.min = f.sec = f.msec = f.usec = f.nsec = 0; break; case STDTIME_FIELD_MIN: if (f.sec >= 30) { f.min++; } f.sec = f.msec = f.usec = f.nsec = 0; break; case STDTIME_FIELD_SEC: if (f.msec >= 500) { f.sec++; } f.msec = f.usec = f.nsec = 0; break; case STDTIME_FIELD_MSEC: if (f.usec >= 500) { f.msec++; } f.usec = f.nsec = 0; break; case STDTIME_FIELD_USEC: if (f.nsec >= 500) { f.usec++; } f.nsec = 0; break; default: STDTIME_RET_EC (unexpected_condition); /* should not occur */ } /* switch (field) */ /* copy back to pDelta parameter, with correct sign */ pDelta->day = sign * f.day; pDelta->hour = sign * f.hour; pDelta->min = sign * f.min; pDelta->sec = sign * f.sec; pDelta->msec = sign * f.msec; pDelta->usec = sign * f.usec; pDelta->nsec = sign * f.nsec; STDTIME_RET_OK; } /* RoundStdTimeDelta */ /*** Delta/Span conversion functions *****************************************/ /*-***************************************************************************/ /* StdTimeDeltaToStdTimeSpan */ /* convert a StdTimeDelta to a StdTimeSPan value */ /* if the resultant value exceeds the maximum range for a span, return */ /* STDTIME_FALSE. the value range is about +/- 8400 years, in terms of */ /* 100-nanosecond intervals. */ /*****************************************************************************/ STDTIME_RC StdTimeDeltaToStdTimeSpan ( const STDTIME_DELTA * /*I*/ pDelta, STDTIME_SPAN * /*O*/ pSpan) { QUADLIB_I64 qSum; QUADLIB_I64 qTest; STDTIME_ENUM_FUNC (StdTimeDeltaToStdTimeSpan) if ((pDelta == NULL) || (pSpan == NULL)) { STDTIME_RET_EC (null_argument); } /* accumulate field values, and convert to nanoseconds */ /* qSum = day */ qSum = QUADLIB_I64_CASTI32 (pDelta->day); /* qSum (day) *= 24 -> (hour)_*/ QUADLIB_I64_MUL_EQ (qSum, QUADLIB_I64_CASTI32 (24)); /* qSum += hour */ QUADLIB_I64_ADD_EQ (qSum, QUADLIB_I64_CASTI32 (pDelta->hour)); /* qSum (hour) *= 60 -> (min) */ QUADLIB_I64_MUL_EQ (qSum, QUADLIB_I64_CASTI32 (60)); /* qSum += min */ QUADLIB_I64_ADD_EQ (qSum, QUADLIB_I64_CASTI32 (pDelta->min)); /* qSum (min) *= 60 -> (sec) */ QUADLIB_I64_MUL_EQ (qSum, QUADLIB_I64_CASTI32 (60)); /* qSum += sec */ QUADLIB_I64_ADD_EQ (qSum, QUADLIB_I64_CASTI32 (pDelta->sec)); /* qSum (sec) *= 1000 -> (msec) */ QUADLIB_I64_MUL_EQ (qSum, QUADLIB_I64_CASTI32 (1000)); /* qSum += msec */ QUADLIB_I64_ADD_EQ (qSum, QUADLIB_I64_CASTI32 (pDelta->msec)); /* qSum (msec) *= 1000 -> (usec) */ QUADLIB_I64_MUL_EQ (qSum, QUADLIB_I64_CASTI32 (1000)); /* qSum += usec */ QUADLIB_I64_ADD_EQ (qSum, QUADLIB_I64_CASTI32 (pDelta->usec)); /* qSum (usec) *= 10 -> (nsec/100) */ QUADLIB_I64_MUL10_EQ (qSum); /* qSum += nsec/100 */ QUADLIB_I64_ADD_EQ (qSum, QUADLIB_I64_CASTI32 ((pDelta->nsec) / 100)); pSpan->value = qSum; if (QUADLIB_I64_LT_0 (qSum)) { /* create negative test value and see if value is less than it */ QUADLIB_U64_HI (qTest) = STDTIME_SPAN_MIN_HI; QUADLIB_U64_LO (qTest) = STDTIME_SPAN_MIN_LO; if (QUADLIB_I64_LT (qSum, qTest)) /* qSum < qTest */ { STDTIME_RET_EC (span_out_of_range); } } else /* qSum >= 0 */ { /* create positive test value and see if value is less than it */ QUADLIB_U64_HI (qTest) = STDTIME_SPAN_MAX_HI; QUADLIB_U64_LO (qTest) = STDTIME_SPAN_MAX_LO; if (QUADLIB_I64_GT (qSum, qTest)) /* qSum > qTest */ { STDTIME_RET_EC (span_out_of_range); } } STDTIME_RET_OK; } /* StdTimeDeltaToStdTimeSpan */ /*-***************************************************************************/ /* StdTimeSpanToStdTimeDelta */ /* convert a StdTimeSpan to a StdTimeDelta value */ /* if the resultant day value exceeds the maximum range for a day, return */ /* STDTIME_FALSE. the value range 8400 years * 366 days, just to keep */ /* the definition simple. */ /*****************************************************************************/ STDTIME_RC StdTimeSpanToStdTimeDelta ( const STDTIME_SPAN * /*I*/ pSpan, STDTIME_DELTA * /*O*/ pDelta) { QUADLIB_U64 uVal; QUADLIB_U64 uMod; QUADLIB_U64 uDiv; int32_t sign = 1; STDTIME_ENUM_FUNC (StdTimeSpanToStdTimeDelta) if ((pSpan == NULL) || (pDelta == NULL)) { STDTIME_RET_EC (null_argument); } if (QUADLIB_I64_LT_0 (pSpan->value)) { uVal = QUADLIB_I64_NEG (pSpan->value); sign = -1; } else { uVal = pSpan->value; } /* uVal has count of 100 nsecs intervals */ /* calculate: nsec = (uVal % 10) * 100; */ QUADLIB_U64_HI (uDiv) = 0; QUADLIB_U64_LO (uDiv) = 10; uVal = QuadLibU64DivMod (uVal, uDiv, &uMod); pDelta->nsec = sign * QUADLIB_I64_LO (uMod) * 100; /* uVal has usecs */ /* calculate: usec = uVal % 1000; */ QUADLIB_U64_LO (uDiv) = 1000; uVal = QuadLibU64DivMod (uVal, uDiv, &uMod); pDelta->usec = sign * QUADLIB_I64_LO (uMod); /* uVal has msecs */ /* calculate: wMilliseconds = uVal % 1000; */ /* QUADLIB_U64_LO (uDiv) = 1000; ==> still in effect */ uVal = QuadLibU64DivMod (uVal, uDiv, &uMod); pDelta->msec = sign * QUADLIB_U64_LO (uMod); /* uVal has secs */ /* calculate: wSecond = uVal % 60; */ QUADLIB_U64_LO (uDiv) = 60; uVal = QuadLibU64DivMod (uVal, uDiv, &uMod); pDelta->sec = sign * QUADLIB_U64_LO (uMod); /* uVal has mins */ /* calculate: wMinute = uVal % 60; */ /* QUADLIB_U64_LO (uDiv) = 60; ==> still in effect */ uVal = QuadLibU64DivMod (uVal, uDiv, &uMod); pDelta->min = sign * QUADLIB_U64_LO(uMod); /* uVal has hours */ /* calculate: wHour = uVal % 24; */ QUADLIB_U64_LO (uDiv) = 24; uVal = QuadLibU64DivMod (uVal, uDiv, &uMod); pDelta->hour = sign * QUADLIB_U64_LO (uMod); /* uVal has days */ pDelta->day = sign * QUADLIB_I64_LO (uVal); if (QUADLIB_I64_OVERFLOW_I32 (uVal)) { STDTIME_RET_EC (overflow_in_delta_day); } /* we check the positive value for valid range, so that +/- cases */ /* are both covered in one test */ if (QUADLIB_I64_LO (uVal) > STDTIME_DELTA_DAY_MAX) { STDTIME_RET_EC (overflow_in_delta_day); } STDTIME_RET_OK; } /* StdTimeSpanToStdTimeDelta */ /*** validation of delta and span ********************************************/ /*-***************************************************************************/ /* ValidStdTimeDelta */ /* validate delta by converting to a span and discard the result */ /*****************************************************************************/ STDTIME_RC ValidStdTimeDelta ( const STDTIME_DELTA * /*I*/ pDelta) { STDTIME_SPAN span; STDTIME_ENUM_FUNC (ValidStdTimeDelta) STDTIME_RET (StdTimeDeltaToStdTimeSpan (pDelta, &span)); } /* ValidStdTimeDelta */ /*-***************************************************************************/ /* ValidStdTimeSpan */ /* validate span by converting to a delta and discard the result */ /*****************************************************************************/ STDTIME_RC ValidStdTimeSpan ( const STDTIME_SPAN * /*I*/ pSpan) { STDTIME_DELTA delta; STDTIME_ENUM_FUNC (ValidStdTimeSpan) STDTIME_RET (StdTimeSpanToStdTimeDelta (pSpan, &delta)); } /* ValidStdTimeSpan */ /*** Delta/Span sign-related functions ***************************************/ /*-***************************************************************************/ /* SgnStdTimeDelta */ /* characterize the sign of a delta as -1, 0 or +1 */ /*****************************************************************************/ int32_t SgnStdTimeDelta ( const STDTIME_DELTA * /*I*/ pDelta) { STDTIME_SPAN span; if (pDelta == NULL) { return STDTIME_ERR; } /* test for a normal sign first */ if ((pDelta->day == 0) && (pDelta->hour == 0) && (pDelta->min == 0) && (pDelta->sec == 0) && (pDelta->msec == 0) && (pDelta->usec == 0) && (pDelta->nsec == 0)) { return STDTIME_EQ; /* == 0 */ } if ((pDelta->day >= 0) && (pDelta->hour >= 0) && (pDelta->min >= 0) && (pDelta->sec >= 0) && (pDelta->msec >= 0) && (pDelta->usec >= 0) && (pDelta->nsec >= 0)) { return STDTIME_GT; /* > 0 (not >= 0, because 0 already checked for) */ } if ((pDelta->day <= 0) && (pDelta->hour <= 0) && (pDelta->min <= 0) && (pDelta->sec <= 0) && (pDelta->msec <= 0) && (pDelta->usec <= 0) && (pDelta->nsec <= 0)) { return STDTIME_LT; /* < 0 (not <= 0, because 0 already checked for) */ } /* delta has a complex sign. convert to a span, and report its sign */ if (StdTimeDeltaToStdTimeSpan (pDelta, &span) == STDTIME_OK) { return SgnStdTimeSpan (&span); } return STDTIME_ERR; } /* SgnStdTimeDelta */ /*-***************************************************************************/ /* AbsStdTimeDelta */ /* form absolute value of a delta; depends on its sign */ /*****************************************************************************/ STDTIME_RC AbsStdTimeDelta ( STDTIME_DELTA * /*IO*/ pDelta) { int32_t sign; STDTIME_ENUM_FUNC (AbsStdTimeDelta) sign = SgnStdTimeDelta (pDelta); /* handles NULL argument */ if (sign == STDTIME_ERR) { STDTIME_RET_EC (failure_in_api_SgnStdTimeDelta); } if (sign == STDTIME_LT) { STDTIME_RET (NegStdTimeDelta (pDelta)); } STDTIME_RET_OK; } /* AbsStdTimeDelta */ /*-***************************************************************************/ /* NegStdTimeDelta */ /* form negative value of a delta; negates each element */ /*****************************************************************************/ STDTIME_RC NegStdTimeDelta ( STDTIME_DELTA * /*IO*/ pDelta) { STDTIME_ENUM_FUNC (NegStdTimeDelta) if (pDelta == NULL) { STDTIME_RET_EC (null_argument); } pDelta->day = -(pDelta->day); pDelta->hour = -(pDelta->hour); pDelta->min = -(pDelta->min); pDelta->sec = -(pDelta->sec); pDelta->msec = -(pDelta->msec); pDelta->usec = -(pDelta->usec); pDelta->nsec = -(pDelta->nsec); STDTIME_RET_OK; } /* NegStdTimeDelta */ /*-***************************************************************************/ /* SgnStdTimeSpan */ /* characterize the sign of a span as -1, 0 or +1 */ /*****************************************************************************/ int32_t SgnStdTimeSpan ( const STDTIME_SPAN * /*I*/ pSpan) { if (pSpan == NULL) { return STDTIME_ERR; } if (QUADLIB_I64_LT_0 (pSpan->value)) { return STDTIME_LT; } if (QUADLIB_I64_EQ_0 (pSpan->value)) { return STDTIME_EQ; } return STDTIME_GT; } /* SgnStdTimeSpan */ /*-***************************************************************************/ /* AbsStdTimeSpan */ /* form absolute value of a span, via call to QUADLIB function */ /*****************************************************************************/ STDTIME_RC AbsStdTimeSpan ( STDTIME_SPAN * /*IO*/ pSpan) { STDTIME_ENUM_FUNC (AbsStdTimeSpan) if (pSpan == NULL) { STDTIME_RET_EC (null_argument); } pSpan->value = QUADLIB_I64_ABS (pSpan->value); STDTIME_RET_OK; } /* AbsStdTimeSpan */ /*-***************************************************************************/ /* NegStdTimeSpan */ /* form negative value of a span, via call to QUADLIB function */ /*****************************************************************************/ STDTIME_RC NegStdTimeSpan ( STDTIME_SPAN * /*IO*/ pSpan) { STDTIME_ENUM_FUNC (NegStdTimeSpan) if (pSpan == NULL) { STDTIME_RET_EC (null_argument); } pSpan->value = QUADLIB_I64_NEG (pSpan->value); STDTIME_RET_OK; } /* NegStdTimeSpan */ /*** Compare functions *******************************************************/ /*-***************************************************************************/ /* CompareStdTime */ /* compare two STDTIME values and return a strcmp-like result */ /*****************************************************************************/ int32_t CompareStdTime ( const STDTIME * /*I*/ pOne, const STDTIME * /*I*/ pTwo) { if ((pOne == NULL) || (pTwo == NULL)) { return STDTIME_ERR; } PVT_STDTIME_COMPARE(dwHighDateTime); PVT_STDTIME_COMPARE(dwLowDateTime); return STDTIME_EQ; } /* CompareStdTime */ /*-***************************************************************************/ /* CompareStdTimeFields */ /* compare two STDTIME_FIELDS values and return a strcmp-like result */ /*****************************************************************************/ int32_t CompareStdTimeFields ( const STDTIME_FIELDS * /*I*/ pOne, const STDTIME_FIELDS * /*I*/ pTwo) { if ((pOne == NULL) || (pTwo == NULL)) { return STDTIME_ERR; } PVT_STDTIME_COMPARE(year); PVT_STDTIME_COMPARE(mon ); PVT_STDTIME_COMPARE(day ); PVT_STDTIME_COMPARE(hour); PVT_STDTIME_COMPARE(min ); PVT_STDTIME_COMPARE(sec ); PVT_STDTIME_COMPARE(msec); PVT_STDTIME_COMPARE(usec); PVT_STDTIME_COMPARE(nsec); return STDTIME_EQ; } /* CompareStdTimeFields */ /*** FileTime functions ******************************************************/ /*-***************************************************************************/ /* StdTimeToFileTime */ /* convert: StdTime structure to FILETIME structure */ /*****************************************************************************/ STDTIME_RC StdTimeToFileTime ( const STDTIME * /*I*/ pStdTime, STDTIME_WIN_FILETIME * /*O*/ pFileTime) { STDTIME_ENUM_FUNC (StdTimeToFileTime) if (pFileTime == NULL || pStdTime == NULL) { STDTIME_RET_EC (null_argument); } /* Structures are now identical, so just copy. */ *pFileTime = *pStdTime; /* faster than memcpy */ STDTIME_RET_OK; } /* StdTimeToFileTime */ /*-***************************************************************************/ /* FileTimeToStdTime */ /* convert: FILETIME structure to StdTime structure */ /*****************************************************************************/ STDTIME_RC FileTimeToStdTime ( const STDTIME_WIN_FILETIME * /*I*/ pFileTime, STDTIME * /*O*/ pStdTime) { STDTIME_ENUM_FUNC (FileTimeToStdTime) if (pFileTime == NULL || pStdTime == NULL) { STDTIME_RET_EC (null_argument); } /* Structures are now identical, so just copy. */ *pStdTime = *pFileTime; /* faster than memcpy */ STDTIME_RET_OK; } /* FileTimeToStdTime */ #if defined(_WIN32) /*-***************************************************************************/ /* StdTimeFieldsToFileTime */ /* convert: StdTimeFields structure to FILETIME structure */ /*****************************************************************************/ STDTIME_RC StdTimeFieldsToFileTime ( const STDTIME_FIELDS * /*I*/ pStdTimeFields, STDTIME_WIN_FILETIME * /*O*/ pFileTime) { STDTIME_WIN_SYSTEMTIME st; STDTIME_WIN_FILETIME ft; QUADLIB_U64 qFileTime; int32_t omit = 0; uint32_t incr32; STDTIME_ENUM_FUNC (StdTimeFieldsToFileTime) if (pFileTime == NULL) { STDTIME_RET_EC (null_argument); } *pFileTime = StdTimeApiZeroFileTime(); if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } /* the 'omit' argument is like NULL, but suppressing rounding */ if STDTIME_IFNOT (StdTimeFieldsToSystemTimeEx ( pStdTimeFields, &st, &omit, &omit)) { STDTIME_RET_RC; } if (! STDTIME_API_SYSTEMTIMETOFILETIME (&st, &ft)) { STDTIME_RET_EC (failure_in_api_SystemTimeToFileTime); } /* SYSTEMTIME does not have usec and nsec, so add them to FILETIME */ QUADLIB_U64_HI (qFileTime) = ft.dwHighDateTime; QUADLIB_U64_LO (qFileTime) = ft.dwLowDateTime; incr32 = (uint32_t) (pStdTimeFields->usec * STDTIME_PREC_NSEC) + (uint32_t) (pStdTimeFields->nsec / STDTIME_SCALE_NSEC); QUADLIB_U64_ADD_EQ (qFileTime, QUADLIB_U64_CASTU32 (incr32)); pFileTime->dwHighDateTime = QUADLIB_U64_HI (qFileTime); pFileTime->dwLowDateTime = QUADLIB_U64_LO (qFileTime); STDTIME_RET_OK; } /* StdTimeFieldsToFileTime */ /*-***************************************************************************/ /* FileTimeToStdTimeFields */ /* convert: FILETIME structure to StdTimeFields structure */ /*****************************************************************************/ STDTIME_RC FileTimeToStdTimeFields ( const STDTIME_WIN_FILETIME * /*I*/ pFileTime, STDTIME_FIELDS * /*O*/ pStdTimeFields) { STDTIME_WIN_SYSTEMTIME st; int32_t usec; int32_t nsec; STDTIME_ENUM_FUNC (FileTimeToStdTimeFields) if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } *pStdTimeFields = ZeroStdTimeFields (); if (pFileTime == NULL) { STDTIME_RET_EC (null_argument); } if STDTIME_IFNOT (Rc_StdTime_FileTimeToSystemTimeEx ( pFileTime, &st, &usec, &nsec)) { STDTIME_RET_RC; } STDTIME_RET (SystemTimeExToStdTimeFields (&st, usec, nsec, pStdTimeFields)); } /* FileTimeToStdTimeFields */ #endif /* defined(_WIN32) */ /*** SystemTimeEx functions **************************************************/ /*-***************************************************************************/ /* StdTimeToSystemTimeEx */ /* convert: StdTime structure to SYSTEMTIME and extra fields */ /* method: StdTime -> FILETIME -> StdTimeFields */ /*****************************************************************************/ STDTIME_RC StdTimeToSystemTimeEx ( const STDTIME * /*I*/ pStdTime, STDTIME_WIN_SYSTEMTIME * /*O*/ pSystemTime, int32_t * /*O*/ pUsec, int32_t * /*O*/ pNsec) { STDTIME_WIN_FILETIME ft; STDTIME_ENUM_FUNC (StdTimeToSystemTimeEx) if (pSystemTime == NULL) { STDTIME_RET_EC (null_argument); } *pSystemTime = StdTimeApiZeroSystemTime(); if STDTIME_IF (StdTimeToFileTime (pStdTime, &ft)) { STDTIME_RET (Rc_StdTime_FileTimeToSystemTimeEx ( &ft, pSystemTime, pUsec, pNsec)); } STDTIME_RET_RC; } /* StdTimeToSystemTimeEx */ /*-***************************************************************************/ /* SystemTimeExToStdTime */ /* convert: SYSTEMTIME and extra fields to StdTime structure */ /* method: SYSTEMTIME, fields -> FILETIME -> StdTime */ /*****************************************************************************/ STDTIME_RC SystemTimeExToStdTime ( const STDTIME_WIN_SYSTEMTIME * /*I*/ pSystemTime, int32_t /*I*/ nUsec, int32_t /*I*/ nNsec, STDTIME * /*O*/ pStdTime) { STDTIME_WIN_FILETIME ft; STDTIME_ENUM_FUNC (SystemTimeExToStdTime) if (pStdTime == NULL) { STDTIME_RET_EC (null_argument); } *pStdTime = ZeroStdTime (); if STDTIME_IF (Rc_StdTime_SystemTimeExToFileTime ( pSystemTime, nUsec, nNsec, &ft)) { STDTIME_RET (FileTimeToStdTime (&ft, pStdTime)); } STDTIME_RET_RC; } /* SystemTimeExToStdTime */ #if defined(_WIN32) /*-***************************************************************************/ /* StdTimeFieldsToSystemTimeEx */ /* convert: StdTimeFields structure to SYSTEMTIME and extra fields */ /*****************************************************************************/ STDTIME_RC StdTimeFieldsToSystemTimeEx ( const STDTIME_FIELDS * /*I*/ pStdTimeFields, STDTIME_WIN_SYSTEMTIME * /*O*/ pSystemTime, int32_t * /*O*/ pUsec, /* optional */ int32_t * /*O*/ pNsec) /* optional */ { int32_t wday; int32_t msec; int32_t usec; int32_t nsec; STDTIME_ENUM_FUNC (StdTimeFieldsToSystemTimeEx) if (pSystemTime == NULL) { STDTIME_RET_EC (null_argument); } *pSystemTime = StdTimeApiZeroSystemTime(); if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } if STDTIME_IFNOT (ValidStdTimeFields (pStdTimeFields)) { STDTIME_RET_RC; } pSystemTime->wYear = (STDTIME_WIN_WORD) pStdTimeFields->year; pSystemTime->wMonth = (STDTIME_WIN_WORD) pStdTimeFields->mon; pSystemTime->wDay = (STDTIME_WIN_WORD) pStdTimeFields->day; pSystemTime->wHour = (STDTIME_WIN_WORD) pStdTimeFields->hour; pSystemTime->wMinute = (STDTIME_WIN_WORD) pStdTimeFields->min; pSystemTime->wSecond = (STDTIME_WIN_WORD) pStdTimeFields->sec; nsec = pStdTimeFields->nsec; usec = pStdTimeFields->usec; msec = pStdTimeFields->msec; /* If pointers not NULL, copy values there. */ if (pNsec) *pNsec = nsec; if (pUsec) *pUsec = usec; pSystemTime->wMilliseconds = (STDTIME_WIN_WORD) msec; #if 0 /* OBSOLETE: delete next release */ Rc_StdTime_NormalizeSystemTimeEx (pSystemTime, pNsec, pUsec); #endif /* generate wDayOfWeek value */ /* a conversion to FILETIME and back to SYSTEM could do this */ /* but that is a lot of overhead for one field */ /* the 'yday' value, stored in a struct tm, is not contained in */ /* a SYSTEMTIME struct, so we discard its value with the NULL parm */ if STDTIME_IFNOT (StdTimeW32GetDayofWeekAndYear ( pStdTimeFields->year, pStdTimeFields->mon, pStdTimeFields->day, &wday, NULL)) { STDTIME_RET_EC (failure_in_api_StdTimeW32GetDayofWeekAndYear); } pSystemTime->wDayOfWeek = (STDTIME_WIN_WORD) wday; STDTIME_RET_OK; } /* StdTimeFieldsToSystemTimeEx */ /*-***************************************************************************/ /* SystemTimeExToStdTimeFields */ /* convert: SYSTEMTIME and extra fields to StdTimeFields structure */ /*****************************************************************************/ STDTIME_RC SystemTimeExToStdTimeFields ( const STDTIME_WIN_SYSTEMTIME * /*I*/ pSystemTime, int32_t /*I*/ nUsec, int32_t /*I*/ nNsec, STDTIME_FIELDS * /*O*/ pStdTimeFields) { STDTIME_ENUM_FUNC (SystemTimeExToStdTimeFields) if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } *pStdTimeFields = ZeroStdTimeFields (); if (pSystemTime == NULL) { STDTIME_RET_EC (null_argument); } pStdTimeFields->year = pSystemTime->wYear; pStdTimeFields->mon = pSystemTime->wMonth; pStdTimeFields->day = pSystemTime->wDay; pStdTimeFields->hour = pSystemTime->wHour; pStdTimeFields->min = pSystemTime->wMinute; pStdTimeFields->sec = pSystemTime->wSecond; pStdTimeFields->msec = pSystemTime->wMilliseconds; pStdTimeFields->usec = 0; pStdTimeFields->nsec = 0; STDTIME_RET (StdTimeFields_AddFraction (pStdTimeFields, 0, nUsec, nNsec)); } /* SystemTimeExToStdTimeFields */ #endif /* defined(_WIN32) */ /*** SystemTime functions ****************************************************/ /*-***************************************************************************/ /* StdTimeToSystemTime */ /* convert: StdTime structure to SYSTEMTIME (without extra fields) */ /*****************************************************************************/ STDTIME_RC StdTimeToSystemTime ( const STDTIME * /*I*/ pStdTime, STDTIME_WIN_SYSTEMTIME * /*O*/ pSystemTime) { STDTIME_ENUM_FUNC (StdTimeToSystemTime) STDTIME_RET (StdTimeToSystemTimeEx (pStdTime, pSystemTime, NULL, NULL)); } /* StdTimeToSystemTime */ /*-***************************************************************************/ /* SystemTimeToStdTime */ /* convert: SYSTEMTIME (without extra fields) to StdTime structure */ /*****************************************************************************/ STDTIME_RC SystemTimeToStdTime ( const STDTIME_WIN_SYSTEMTIME * /*I*/ pSystemTime, STDTIME * /*O*/ pStdTime) { STDTIME_ENUM_FUNC (SystemTimeToStdTime) STDTIME_RET (SystemTimeExToStdTime (pSystemTime, 0, 0, pStdTime)); } /* SystemTimeToStdTime */ #ifdef _WIN32 /* DbTimeStamp functions only for _WIN32 */ /*** DbTimeStamp functions ***************************************************/ /*-***************************************************************************/ /* StdTimeApiZeroDbTimeStamp */ /* return a zero value of type STDTIME_WIN_DBTIMESTAMP */ /*****************************************************************************/ STDTIME_WIN_DBTIMESTAMP StdTimeApiZeroDbTimeStamp () { STDTIME_WIN_DBTIMESTAMP zero = {0}; return zero; } /* StdTimeApiZeroDbTimeStamp */ /*-***************************************************************************/ /* StdTimeToDbTimeStamp */ /* convert: StdTime structure to DBTIMESTAMP */ /*****************************************************************************/ STDTIME_RC StdTimeToDbTimeStamp ( const STDTIME * /*I*/ pStdTime, STDTIME_WIN_DBTIMESTAMP * /*O*/ pDbTimeStamp) { STDTIME_FIELDS stdTimeFields; STDTIME_ENUM_FUNC (StdTimeToDbTimeStamp) if (pDbTimeStamp == NULL) { STDTIME_RET_EC (null_argument); } *pDbTimeStamp = StdTimeApiZeroDbTimeStamp(); if STDTIME_IF (StdTimeToStdTimeFields (pStdTime, &stdTimeFields)) { STDTIME_RET (StdTimeFieldsToDbTimeStamp (&stdTimeFields, pDbTimeStamp)); } STDTIME_RET_RC; } /* StdTimeToDbTimeStamp */ /*-***************************************************************************/ /* DbTimeStampToStdTime */ /* convert: DBTIMESTAMP to StdTime structure */ /*****************************************************************************/ STDTIME_RC DbTimeStampToStdTime ( const STDTIME_WIN_DBTIMESTAMP * /*I*/ pDbTimeStamp, STDTIME * /*O*/ pStdTime) { STDTIME_FIELDS stdTimeFields; STDTIME_ENUM_FUNC (DbTimeStampToStdTime) if (pStdTime == NULL) { STDTIME_RET_EC (null_argument); } *pStdTime = ZeroStdTime (); if STDTIME_IF (DbTimeStampToStdTimeFields (pDbTimeStamp, &stdTimeFields)) { STDTIME_RET (StdTimeFieldsToStdTime (&stdTimeFields, pStdTime)); } STDTIME_RET_RC; } /* DbTimeStampToStdTime */ /*-***************************************************************************/ /* StdTimeFieldsToDbTimeStamp */ /* convert: StdTimeFields structure to DBTIMESTAMP */ /*****************************************************************************/ STDTIME_RC StdTimeFieldsToDbTimeStamp ( const STDTIME_FIELDS * /*I*/ pStdTimeFields, STDTIME_WIN_DBTIMESTAMP * /*O*/ pDbTimeStamp) { STDTIME_ENUM_FUNC (StdTimeFieldsToDbTimeStamp) if (pDbTimeStamp == NULL) { STDTIME_RET_EC (null_argument); } *pDbTimeStamp = StdTimeApiZeroDbTimeStamp(); if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } if STDTIME_IFNOT (ValidStdTimeFields (pStdTimeFields)) { STDTIME_RET_RC; } pDbTimeStamp->year = (STDTIME_WIN_SHORT) pStdTimeFields->year; pDbTimeStamp->month = (STDTIME_WIN_USHORT) pStdTimeFields->mon; pDbTimeStamp->day = (STDTIME_WIN_USHORT) pStdTimeFields->day; pDbTimeStamp->hour = (STDTIME_WIN_USHORT) pStdTimeFields->hour; pDbTimeStamp->minute = (STDTIME_WIN_USHORT) pStdTimeFields->min; pDbTimeStamp->second = (STDTIME_WIN_USHORT) pStdTimeFields->sec; /* a DBTIMESTAMP fraction is a number of nanoseconds, 0 to 999,999,999 */ pDbTimeStamp->fraction = (STDTIME_WIN_ULONG) (pStdTimeFields->msec * 1000000) + (pStdTimeFields->usec * 1000) + (pStdTimeFields->nsec); STDTIME_RET_OK; } /* StdTimeFieldsToDbTimeStamp */ /*-***************************************************************************/ /* DbTimeStampToStdTimeFields */ /* convert: DBTIMESTAMP to StdTimeFields structure */ /*****************************************************************************/ STDTIME_RC DbTimeStampToStdTimeFields ( const STDTIME_WIN_DBTIMESTAMP * /*I*/ pDbTimeStamp, STDTIME_FIELDS * /*O*/ pStdTimeFields) { int32_t fraction; STDTIME_ENUM_FUNC (DbTimeStampToStdTimeFields) if (pStdTimeFields == NULL) { STDTIME_RET_EC (null_argument); } *pStdTimeFields = ZeroStdTimeFields (); if (pDbTimeStamp == NULL) { STDTIME_RET_EC (null_argument); } pStdTimeFields->year = (int32_t) pDbTimeStamp->year; pStdTimeFields->mon = (int32_t) pDbTimeStamp->month; pStdTimeFields->day = (int32_t) pDbTimeStamp->day; pStdTimeFields->hour = (int32_t) pDbTimeStamp->hour; pStdTimeFields->min = (int32_t) pDbTimeStamp->minute; pStdTimeFields->sec = (int32_t) pDbTimeStamp->second; fraction = (int32_t) pDbTimeStamp->fraction; /* limit fraction to valid range */ if (fraction < 0) { fraction = 0; } else if (fraction > 999999999) { fraction = 999999999; } pStdTimeFields->msec = (fraction / 1000000) % 1000; pStdTimeFields->usec = (fraction / 1000) % 1000; pStdTimeFields->nsec = (fraction % 1000); STDTIME_RET_OK; } /* DbTimeStampToStdTimeFields */ #endif /* DbTimeStamp functions only for _WIN32 */ /*** StructTmEx functions ****************************************************/ /*-***************************************************************************/ /* StdTimeToStructTmEx */ /* convert: StdTime structure to 'struct tm' and extra fields */ /*****************************************************************************/ STDTIME_RC StdTimeToStructTmEx ( const STDTIME * /*I*/ pStdTime, struct tm * /*O*/ pStructTm, int32_t * /*O*/ pNsec) /* optional */ { time_t timet; int32_t nsec; STDTIME_ENUM_FUNC (StdTimeToStructTmEx) rc = StdTimeToTimeTypeEx (pStdTime, &timet, &nsec); if (rc == STDTIME_OK) { if (Bool_StdTimeGmTimeR (&timet, pStructTm) != STDTIME_TRUE) STDTIME_RET_EC (argument_out_of_range); } if (pNsec) *pNsec = nsec; STDTIME_RET_RC; } /* StdTimeToStructTmEx */ /*-***************************************************************************/ /* StructTmExToStdTime */ /* convert: 'struct tm' and extra fields to StdTime structure */ /*****************************************************************************/ STDTIME_RC StructTmExToStdTime ( const struct tm * /*I*/ pStructTm, int32_t /*I*/ nNsec, STDTIME * /*O*/ pStdTime) { time_t timet; STDTIME_ENUM_FUNC (StructTmExToStdTime) /* usr_mkgmtime doesn't accept const arg so must cast first. */ if ((timet = usr_mkgmtime ((struct tm *) pStructTm)) < 0) { STDTIME_RET_EC (argument_out_of_range); } STDTIME_RET (TimeTypeExToStdTime (timet, nNsec, pStdTime)); } /* StructTmExToStdTime */ /*** TimeTypeEx functions ************************************************ */ /************************************************************************/ /* StdTimeToTimeTypeEx */ /* convert: StdTime struct to time_t plus nanoseconds */ /************************************************************************/ STDTIME_RC StdTimeToTimeTypeEx ( const STDTIME * /*I*/ pStdTime, time_t * /*O*/ pTimeT, int32_t * /*O*/ pNsec) { QUADLIB_U64 qFileTime; QUADLIB_U64 qValue, qOffset; QUADLIB_U64 qRemainder; int32_t nsec; STDTIME_ENUM_FUNC (StdTimeToTimeTypeEx) /* Check that *pStdTime is not before 1970 (illegal). */ if (pStdTime->dwHighDateTime < FILETIME_1970_HIGH || (pStdTime->dwHighDateTime == FILETIME_1970_HIGH && pStdTime->dwLowDateTime < FILETIME_1970_LOW)) STDTIME_RET_EC (argument_out_of_range); FILETIME_TO_QUADLIB_U64 (pStdTime, &qFileTime); /* This just divides by 10000000 to get seconds since 1601 */ qValue = QUADLIB_I64_DIV ( qFileTime, QUADLIB_I64_CASTI32 (PVT_STDTIME_SCALE_FILETIME) ); /* Get remainder. */ qRemainder = QUADLIB_I64_MOD ( qFileTime, QUADLIB_I64_CASTI32 (PVT_STDTIME_SCALE_FILETIME) ); /* Convert remainder to nanoseconds */ nsec = (QUADLIB_I32_CASTI64 (qRemainder)) * 100; if (pNsec != NULL) *pNsec = nsec; /* Subtract number of seconds from 1601 to 1970. */ QUADLIB_U64_HI(qOffset) = SECONDS_FROM_1601_TO_1970_HIGH; QUADLIB_U64_LO(qOffset) = SECONDS_FROM_1601_TO_1970_LOW; QUADLIB_I64_SUB_EQ (qValue, qOffset); /* modifies qValue */ /* Cast to time_t. */ #ifdef STDTIME_TIME_T64_ENABLED *pTimeT = QUADLIB_I64N_CASTI64(qValue); #else /* DEBUG: should check for qValue out of range before this cast.*/ *pTimeT = QUADLIB_I32_CASTI64 (qValue); #endif return (STDTIME_OK); } /* StdTimeToTimeTypeEx */ /************************************************************************/ /* TimeTypeExToStdTime */ /* convert: time_t plus nanoseconds to StdTime struct */ /************************************************************************/ STDTIME_RC TimeTypeExToStdTime ( time_t /*I*/ timet, int32_t /*I*/ nsec, STDTIME * /*O*/ pStdTime) { QUADLIB_U64 qValue, qOffset; STDTIME_ENUM_FUNC (TimeTypeExToStdTime) if (pStdTime == NULL) { STDTIME_RET_EC (null_argument); } if (timet < 0) { STDTIME_RET_EC (argument_out_of_range); } #ifdef STDTIME_TIME_T64_ENABLED QUADLIB_U64_HI(qValue) = QUADLIB_U64_HI(timet); QUADLIB_U64_LO(qValue) = QUADLIB_U64_LO(timet); #else QUADLIB_U64_HI(qValue) = 0; QUADLIB_U64_LO(qValue) = timet; #endif /* Add number of seconds from 1601 to 1970. */ QUADLIB_U64_HI(qOffset) = SECONDS_FROM_1601_TO_1970_HIGH; QUADLIB_U64_LO(qOffset) = SECONDS_FROM_1601_TO_1970_LOW; QUADLIB_I64_ADD_EQ (qValue, qOffset); /* modifies qValue */ /* Multiply seconds by 10000000 */ qValue = QUADLIB_I64_MUL ( qValue, QUADLIB_I64_CASTI32 (PVT_STDTIME_SCALE_FILETIME) ); /* Divide nsec by 100 to get num of "100-nanoseccond" intervals & add them.*/ QUADLIB_U64_HI(qOffset) = 0; QUADLIB_U64_LO(qOffset) = nsec / 100; QUADLIB_I64_ADD_EQ (qValue, qOffset); /* modifies qValue */ QUADLIB_I64_TO_FILETIME (&qValue, pStdTime); return (STDTIME_OK); } /* TimeTypeExToStdTime */ /*** Constructor-like functions **********************************************/ /*-***************************************************************************/ /* StdTimeFields */ /* construct a STDTIME_FIELDS structure from parameters */ /*****************************************************************************/ STDTIME_FIELDS StdTimeFields ( int32_t /*I*/ year, int32_t /*I*/ mon, int32_t /*I*/ day, int32_t /*I*/ hour, int32_t /*I*/ min, int32_t /*I*/ sec, int32_t /*I*/ msec, int32_t /*I*/ usec, int32_t /*I*/ nsec) { STDTIME_FIELDS x; x.year = year; x.mon = mon; x.day = day; x.hour = hour; x.min = min; x.sec = sec; x.msec = msec; x.usec = usec; x.nsec = nsec; return x; } /* StdTimeFields */ /*-***************************************************************************/ /* StdTimeDelta */ /* construct a STDTIME_DELTA structure from parameters */ /*****************************************************************************/ STDTIME_DELTA StdTimeDelta ( int32_t /*I*/ day, int32_t /*I*/ hour, int32_t /*I*/ min, int32_t /*I*/ sec, int32_t /*I*/ msec, int32_t /*I*/ usec, int32_t /*I*/ nsec) { STDTIME_DELTA x; x.day = day; x.hour = hour; x.min = min; x.sec = sec; x.msec = msec; x.usec = usec; x.nsec = nsec; return x; } /* StdTimeDelta */ /*-***************************************************************************/ /* StdTimeSpan */ /* construct a STDTIME structure from parameters */ /*****************************************************************************/ STDTIME_SPAN StdTimeSpan ( QUADLIB_I64 /*I*/ value) { STDTIME_SPAN x; x.value = value; return x; } /* StdTimeSpan */ /*** Initializer functions ***************************************************/ /*-***************************************************************************/ /* ZeroStdTime */ /* return a zero value of type STDTIME */ /*****************************************************************************/ STDTIME ZeroStdTime () { STDTIME zero = {0}; return zero; } /* ZeroStdTime */ /*-***************************************************************************/ /* ZeroStdTimeFields */ /* return a zero value of type STDTIME_FIELDS */ /*****************************************************************************/ STDTIME_FIELDS ZeroStdTimeFields () { STDTIME_FIELDS zero = {0}; return zero; } /* ZeroStdTimeFields */ /*-***************************************************************************/ /* ZeroStdTimeStringA */ /* return a zero value of type STDTIME_STRINGA */ /*****************************************************************************/ STDTIME_STRINGA ZeroStdTimeStringA () { STDTIME_STRINGA zero = {{0}}; return zero; } /* ZeroStdTimeStringA */ /*-***************************************************************************/ /* ZeroStdTimeDelta */ /* return a zero value of type STDTIME_DELTA */ /*****************************************************************************/ STDTIME_DELTA ZeroStdTimeDelta () { STDTIME_DELTA zero = {0}; return zero; } /* ZeroStdTimeDelta */ /*-***************************************************************************/ /* ZeroStdTimeSpan */ /* return a zero value of type STDTIME_SPAN */ /*****************************************************************************/ STDTIME_SPAN ZeroStdTimeSpan () { STDTIME_SPAN zero = {0}; return zero; } /* ZeroStdTimeSpan */ /*-***************************************************************************/ /* ZeroStdTimeTzoA */ /* return a zero value of type STDTIME_TZOA */ /*****************************************************************************/ STDTIME_TZOA ZeroStdTimeTzoA () { STDTIME_TZOA zero = {{0}}; return zero; } /* ZeroStdTimeTzoA */ /*-***************************************************************************/ /* ZeroStdTimeErrMsgA */ /* return a zero value of type STDTIME_ERRMSGA */ /*****************************************************************************/ STDTIME_ERRMSGA ZeroStdTimeErrMsgA () { STDTIME_ERRMSGA zero = {{0}}; return zero; } /* ZeroStdTimeErrMsgA */ /*-***************************************************************************/ /* ZeroStdTimeErrMsgW */ /*** Initializer functions for 'outside' types *******************************/ /*** names prefixed with 'StdTimeApi' because they are non-STDTIME structs ***/ /*-***************************************************************************/ /* StdTimeApiZeroFileTime */ /* return a zero value of type STDTIME_WIN_FILETIME */ /*****************************************************************************/ STDTIME_WIN_FILETIME StdTimeApiZeroFileTime () { STDTIME_WIN_FILETIME zero = {0}; return zero; } /* StdTimeApiZeroFileTime */ /*-***************************************************************************/ /* StdTimeApiZeroSystemTime */ /* return a zero value of type STDTIME_WIN_SYSTEMTIME */ /*****************************************************************************/ STDTIME_WIN_SYSTEMTIME StdTimeApiZeroSystemTime () { STDTIME_WIN_SYSTEMTIME zero = {0}; return zero; } /* StdTimeApiZeroSystemTime */ /*-***************************************************************************/ /* StdTimeApiZeroStructTm */ /* return a zero value of type struct tm */ /*****************************************************************************/ struct tm StdTimeApiZeroStructTm () { struct tm zero = {0}; return zero; } /* StdTimeApiZeroStructTm */ /*** calandar function interface *********************************************/ /*-***************************************************************************/ /* OBTAIN DAY OF WEEK AND DAY OF WEEK */ /* This provides an interface to the W32 layer, which we do not want to */ /* expose to the user. */ /*****************************************************************************/ STDTIME_RC StdTimeGetDayofWeekAndYear ( int32_t /*I*/ nYear, int32_t /*I*/ nMon, int32_t /*I*/ nDayofMon, int32_t * /*O*/ pDayofWeek, int32_t * /*O*/ pDayofYear) { STDTIME_ENUM_FUNC (StdTimeGetDayofWeekAndYear) STDTIME_RET (StdTimeW32GetDayofWeekAndYear ( nYear, nMon, nDayofMon, pDayofWeek, pDayofYear)); } #if defined(_WIN32) /*** Date functions (COM DATE) ***********************************************/ /************************************************************************/ /* FileTimeToDate */ /* RETURNS: 0=SUCCESS, 1=FAILURE */ /************************************************************************/ static int FileTimeToDate(const FILETIME *pft, double *pdate) { /* Don't allow FILETIME before 1899 (causes negative DATE). */ if (*(__int64 *)pft < 0x014f35a9a90cc000) { *pdate = 0.0; /* set to bad value in case caller ignores return*/ return (1); /* error */ } *pdate = (double)((double)(*(__int64 *)pft) / 8.64e11) - (double)(363 + (1899 - 1601) * 365 + (24 + 24 + 24)); assert (*pdate >= 0.0); return (0); /* success */ } /************************************************************************/ /* DateToFileTime */ /* RETURNS: 0=SUCCESS, 1=FAILURE */ /************************************************************************/ static int DateToFileTime(const double *pdate, FILETIME *pft) { __int64 temp; if (*pdate < 0.0) /* negative date too complicated to deal with */ { temp = 0; *pft = *(FILETIME *)&temp; /* set to bad value in case caller ignores return*/ return (1); /* error */ } /* NOTE: this casts double to _int64 (some precision lost). */ temp = (__int64)((*pdate + (double)(363 + (1899 - 1601) * 365 + (24 + 24 + 24))) * 8.64e11); *pft = *(FILETIME *)&temp; return (0); /* success */ } /*-***************************************************************************/ /* StdTimeToDate */ /* convert: StdTime structure to DATE time value */ /*****************************************************************************/ STDTIME_RC StdTimeToDate ( const STDTIME * /*I*/ pStdTime, STDTIME_WIN_DATE * /*O*/ pDate) { STDTIME_ENUM_FUNC (StdTimeToDate) /* STDTIME same as FILETIME, so this works. */ if (FileTimeToDate (pStdTime, pDate)) STDTIME_RET_EC (argument_out_of_range); STDTIME_RET_OK; } /* StdTimeToDate */ /*-***************************************************************************/ /* DateToStdTime */ /* convert: DATE time value to StdTime structure */ /*****************************************************************************/ STDTIME_RC DateToStdTime ( const STDTIME_WIN_DATE * /*I*/ pDate, STDTIME * /*O*/ pStdTime) { STDTIME_ENUM_FUNC (DateToStdTime) /* STDTIME same as FILETIME, so this works. */ if (DateToFileTime (pDate, pStdTime)) STDTIME_RET_EC (argument_out_of_range); STDTIME_RET_OK; } /* DateToStdTime */ /*-***************************************************************************/ /* StdTimeFieldsToDate */ /* convert: StdTime structure to DATE time value */ /*****************************************************************************/ STDTIME_RC StdTimeFieldsToDate ( const STDTIME_FIELDS * /*I*/ pStdTimeFields, STDTIME_WIN_DATE * /*O*/ pDate) { STDTIME StdTime; STDTIME_ENUM_FUNC (StdTimeFieldsToDate) rc = StdTimeFieldsToStdTime (pStdTimeFields, &StdTime); if (rc == STDTIME_OK) rc = StdTimeToDate (&StdTime, pDate); return (rc); } /* StdTimeFieldsToDate */ /*-***************************************************************************/ /* DateToStdTimeFields */ /* convert: DATE time value to StdTimeFields structure */ /*****************************************************************************/ STDTIME_RC DateToStdTimeFields ( const STDTIME_WIN_DATE * /*I*/ pDate, STDTIME_FIELDS * /*O*/ pStdTimeFields) { STDTIME StdTime; STDTIME_ENUM_FUNC (DateToStdTimeFields) rc = DateToStdTime (pDate, &StdTime); if (rc == STDTIME_OK) rc = StdTimeToStdTimeFields (&StdTime, pStdTimeFields); return (rc); } /* DateToStdTimeFields */ #endif /* defined(_WIN32) */ /*-***************************************************************************/ /* StdTimeToLocalStdTime */ /* convert: StdTime structure from UTC/GMT time to local time */ /* if not using TZDB, use local host to speed up conversion */ /*****************************************************************************/ STDTIME_RC StdTimeToLocalStdTime ( const STDTIME * /*I*/ pStdTime, STDTIME * /*O*/ pStdTimeLocal) { STDTIME_ENUM_FUNC (StdTimeToLocalStdTime) { STDTIME_WIN_FILETIME gmtFileTime; STDTIME_WIN_FILETIME locFileTime; if STDTIME_IFNOT (StdTimeToFileTime (pStdTime, &gmtFileTime)) { STDTIME_RET_RC; } if (! STDTIME_API_FILETIMETOLOCALFILETIME (&gmtFileTime, &locFileTime)) { STDTIME_RET_EC (failure_in_api_FileTimeToLocalFileTime); } STDTIME_RET (FileTimeToStdTime (&locFileTime, pStdTimeLocal)); } } /* StdTimeToLocalStdTime */ /*-***************************************************************************/ /* LocalStdTimeToStdTime */ /* convert: StdTime structure from local time to UTC/GMT time */ /* if not using TZDB, use local host to speed up conversion */ /*****************************************************************************/ STDTIME_RC LocalStdTimeToStdTime ( const STDTIME * /*I*/ pStdTimeLocal, STDTIME * /*O*/ pStdTime) { STDTIME_ENUM_FUNC (LocalStdTimeToStdTime) { STDTIME_WIN_FILETIME gmtFileTime; STDTIME_WIN_FILETIME locFileTime; if STDTIME_IFNOT (StdTimeToFileTime (pStdTimeLocal, &locFileTime)) { STDTIME_RET_RC; } if (! STDTIME_API_LOCALFILETIMETOFILETIME (&locFileTime, &gmtFileTime)) { STDTIME_RET_EC (failure_in_api_LocalFileTimeToFileTime); } STDTIME_RET (FileTimeToStdTime (&gmtFileTime, pStdTime)); } } /* LocalStdTimeToStdTime */ /*** timezone-offset support functions ***************************************/ /*-***************************************************************************/ /* NormalizeStdTimeTzoA */ /* if timezone offset string is in a valid format, normalize it. */ /* normalization involves the following: */ /* */ /* 1. a 'z' code is converted to 'Z' */ /* 2. if the ss field is zero, it is dropped */ /* 3. if all numeric fields are zero, the offset string becomes Z */ /* 4. trailing blanks are removed, and replaced by nuls */ /* */ /*****************************************************************************/ STDTIME_RC NormalizeStdTimeTzoA ( STDTIME_TZOA * /*IO*/ pTzo) { STDTIME_TZOA tzoA = {{0}}; int32_t hour; int32_t min; int32_t sec; STDTIME_ENUM_FUNC (NormalizeStdTimeTzoA) if (pTzo == NULL) { STDTIME_RET_EC (null_argument); } /* limit input tzo string to declared length, jic */ pTzo->str[STDTIME_TZO_LEN] = 0; /* see if timezone offset is omitted */ if (Bool_StdTime_IsBlankA (pTzo->str)) { *pTzo = tzoA; /* clear out entire TZO field */ STDTIME_RET_OK; } if ((pTzo->str[0] == 'z') || (pTzo->str[0] == 'Z')) { if (Bool_StdTime_IsBlankA (pTzo->str+1)) { /* normalize Z and pad field with nuls */ tzoA.str[0] = 'Z'; *pTzo = tzoA; STDTIME_RET_OK; } } if ((pTzo->str[0] != '+') && (pTzo->str[0] != '-')) { STDTIME_RET_EC (invalid_timezone_offset); } if ( (! isdigit (pTzo->str[1])) || (! isdigit (pTzo->str[2])) || ( pTzo->str[3] != ':') || (! isdigit (pTzo->str[4])) || (! isdigit (pTzo->str[5])) ) { STDTIME_RET_EC (invalid_timezone_offset); } hour = atoi (pTzo->str+1); min = atoi (pTzo->str+4); if (! STDTIME_IN_RANGE (hour, 0, 23)) { STDTIME_RET_EC (invalid_hour); } if (! STDTIME_IN_RANGE (min, 0, 59)) { STDTIME_RET_EC (invalid_min); } /* determine whether we have short form or long form */ /* long form: validate 'ss' digits */ if (pTzo->str[6] == ':') { if ( (! isdigit (pTzo->str[7])) || (! isdigit (pTzo->str[8])) ) { STDTIME_RET_EC (invalid_timezone_offset); } sec = atoi (pTzo->str+7); if (! STDTIME_IN_RANGE (sec, 0, 59)) { STDTIME_RET_EC (invalid_sec); } if (! Bool_StdTime_IsBlankA (pTzo->str+9)) { STDTIME_RET_EC (invalid_timezone_offset); } } /* short form: ensure we are at end of string */ else { if (! Bool_StdTime_IsBlankA (pTzo->str+6)) { STDTIME_RET_EC (invalid_timezone_offset); } sec = 0; } /* now, normalize the format */ if ((hour == 0) && (min == 0) && (sec == 0)) { /* if all fields zero, it is GMT time */ /* normalize Z and pad field with nuls */ tzoA.str[0] = 'Z'; *pTzo = tzoA; STDTIME_RET_OK; } if (sec == 0) { /* +hh:mm or -hh:mm format */ sprintf (tzoA.str, "%c%02d:%02d", pTzo->str[0], hour, min); } else { /* +hh:mm:ss or -hh:mm:ss format */ sprintf (tzoA.str, "%c%02d:%02d:%02d", pTzo->str[0], hour, min, sec); } *pTzo = tzoA; /* copy back reformatted tzoA */ STDTIME_RET_OK; } /* NormalizeStdTimeTzoA */ /*** error message decoding functions ****************************************/ /*-***************************************************************************/ /* StdTimeRcToErrMsgA */ /* interpret STDTIME_RC value and convert to a STDTIME_ERRMSGA structure. */ /*****************************************************************************/ STDTIME_RC StdTimeRcToErrMsgA ( const STDTIME_RC /*I*/ stdTimeRc, STDTIME_ERRMSGA * /*O*/ pErrMsg) { STDTIME_ERRMSGA errMsg = {{0}}; int32_t hilevel; int32_t lolevel; int32_t reason; int32_t i; STDTIME_ENUM_FUNC (StdTimeRcToErrMsgA) if (pErrMsg == NULL) { STDTIME_RET_EC (null_argument); } *pErrMsg = ZeroStdTimeErrMsgA (); /* a STDTIME_RC has the following bit layout */ /* bits 31-30(2) unused */ /* bits 29-20(10) hi-level function enum */ /* bits 19-10(10) lo-level function enum */ /* bits 09-00(10) error reason code */ hilevel = (stdTimeRc >> 20) & 0x3FF; lolevel = (stdTimeRc >> 10) & 0x3FF; reason = stdTimeRc & 0x3FF; /* an error code has the form 0xNNNNNNNN = HHHH.LLLL.RRRR# */ /* 0123456789012345678901234567 */ /* this holds a raw hex RC value, and the RC as decimal subfields */ sprintf (errMsg.errcode, "0x%08X = %04d.%04d.%04d", stdTimeRc, hilevel, lolevel, reason); if ((stdTimeRc & 0xC0000000) != 0) { strncpy (errMsg.hilevel, "*** invalid STDTIME_RC value", STDTIME_ERRMSG_LEN); *pErrMsg = errMsg; /* pass back result */ STDTIME_RET_EC (invalid_stdtime_rc_value); } /* the first entry is at STDTIME_FUNC__0000 with an enum value of 0 */ /* enum 0 is "valid" but 0 is not used for error return codes, because */ /* a STDTIME_RC of 0 means there is no error, so 0 is skipped. */ /* example: suppose there were 10 entries; they would have enum's 1 to 10 */ /* the last entry is at STDTIME_FUNC__SIZE with (example) enum value of 11 */ if (hilevel < (STDTIME_RC) STDTIME_FUNC__SIZE) { strncpy (errMsg.hilevel, enum_stdtime_func_text[hilevel], STDTIME_ERRMSG_LEN); } else { strncpy (errMsg.hilevel, "*** undefined hilevel function", STDTIME_ERRMSG_LEN); } if (lolevel < (STDTIME_RC) STDTIME_FUNC__SIZE) { strncpy (errMsg.lolevel, enum_stdtime_func_text[lolevel], STDTIME_ERRMSG_LEN); } else { strncpy (errMsg.lolevel, "*** undefined lolevel function", STDTIME_ERRMSG_LEN); } if (reason < (STDTIME_RC) STDTIME_ERR__SIZE) { strncpy (errMsg.reason, enum_stdtime_err_text[reason], STDTIME_ERRMSG_LEN); } else { strncpy (errMsg.reason, "*** undefined reason code", STDTIME_ERRMSG_LEN); } errMsg.hilevel[STDTIME_ERRMSG_LEN] = 0; errMsg.lolevel[STDTIME_ERRMSG_LEN] = 0; errMsg.reason [STDTIME_ERRMSG_LEN] = 0; /* remove underscores from reason message */ /* the underscores were necessary for the macro to form an enum name */ /* but we don't want them literally present in the message */ for (i=0; errMsg.reason[i]; i++) { if (errMsg.reason[i] == '_') { errMsg.reason[i] = ' '; } } *pErrMsg = errMsg; /* pass back result */ STDTIME_RET_RC; } /* StdTimeRcToErrMsgA */ /*** END - STDTIME.C *********************************************************/