3816 lines
118 KiB
C
3816 lines
118 KiB
C
/*+***************************************************************************/
|
|
/* 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 <math.h>
|
|
#include <assert.h>
|
|
|
|
/*** 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 *********************************************************/
|
|
|
|
|