Files
microser/mmslib/util/time_str.c
2026-06-15 15:48:16 +08:00

2749 lines
80 KiB
C

/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 1994 - 2008, All Rights Reserved */
/* */
/* MODULE NAME : time_str.c */
/* PRODUCT(S) : */
/* */
/* MODULE DESCRIPTION : */
/* */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
/* */
/* MODIFICATION LOG : */
/* Date Who Rev Comments */
/* -------- --- ------ ------------------------------------------- */
/* 06/24/08 EJV 41 Btime6StringToVals CORR: *1000 was missing. */
/* 02/28/08 JRB 40 GetTimeAndUsec ONLY for _WIN32, linux, QNX. */
/* 09/20/07 nav 39 Put TimeAccuracy in UTC Quality string */
/* 02/15/07 RLH 38 Add validity checks to UtcValueToXmlString */
/* so that invalid data will not cause a bad */
/* string to be generated. */
/* 01/16/07 RLH 37 Comment-out code obsoleted by rev 36 */
/* 11/29/06 RLH 36 Merge time_str.c and time_str2, adjust revs, */
/* add portable usr_mkgmtime function, */
/* rewrite date and time parsing functions */
/* tstrStringToTime, tstrStringToTm; */
/* remove stdtime.h include, add validation to */
/* quality-bit string, make date-field ordering */
/* (m-d-y, d-m-y, y-m-d) based on locale, not */
/* always assuming US m-d-y ordering. improve */
/* compliance with use of standard SISCO types. */
/* 09/13/06 NAV 35 Fix quality bits in UTC conversion */
/* 07/13/06 MDE 34 Fixed compile warning */
/* 06/05/06 RLH 33 Change to stdtime include */
/* 03/06/06 RLH 32 add GetTimeAndUsec, tstrTimeToStringGmt, */
/* tstrStringToTimeGmt */
/* 02/28/06 EJV 31 UtcStringToVals: added cast. */
/* 01/30/06 GLB 30 Integrated porting changes for VMS */
/* 02/13/06 DSF 29 Migrate to VS.NET 2005 */
/* 02/03/06 NAV 28 UTC - use 0x1000000 instead of 0xFFFFFF */
/* 01/30/06 GLB 27 Integrated porting changes for VMS */
/* 07/27/04 DWL 26 Added tstrTmToString (struct tm) */
/* 07/16/04 DWL 25 Added tstrStringToTm (struct tm) */
/* 01/27/04 nav 24 UtcValsToString: check for gmtime failure */
/* 12/09/03 JRB 24 Btime6String..: stop using "daylight" global,*/
/* use tm_isdst=0 to treat input as std time. */
/* 10/30/03 GLB & 23 Reworked "XmlStringToUtcValue" & */
/* EJV "UtcValueToXmlString" */
/* 10/24/03 DSF 22 Fixed SX_DATE_TIME functions */
/* 10/15/03 JRB 21 Del _WIN32 ifdef. */
/* 09/01/03 GLB 20 Added "XmlStringToUtcValue" & */
/* "UtcValueToXmlString" */
/* 06/03/03 DSF 19 More compiler warnings */
/* 04/14/03 JRB 18 Eliminate compiler warnings. */
/* 09/26/02 NAV 17 UtcValsToString: use gmtime */
/* 08/05/02 NAV 16 Make Btime4 conversion funcs ansi compatible */
/* 07/12/02 NAV 15 Add UtcValsToString and UtcStringToVals */
/* 06/07/02 NAV 14 sprintf msec to %03d */
/* 02/25/02 JRB 13 Don't use "daylight" global on VXWORKS. */
/* Fix "thisFil..", sprintf call. Add str_util.h*/
/* 12/27/01 GLB 12 Remove embedded comment in prior log 11 */
/* 12/19/01 EJV 11 Converted comments from double slash to */
/* slash star */
/* Substituted AXS4_LOG_xxx with SLOGALWAYS. */
/* Replaced sys includes with sysincs.h */
/* 06/26/01 EJV 10 Eliminated globals to make thread safe. */
/* 02/02/01 EJV 09 tstrStringToTime: chg (long *) to (time_t *) */
/* 11/21/00 MDE 08 Changes for QNX (Btime4 for Win32 only) */
/* 07/17/98 NAV 07 Modify century calculation for y2k */
/* 10/15/97 NAV 06 Add Btime4 Support Functions */
/* 10/08/97 NAV 05 Add seconds to Btime4 and Gtime */
/* 10/06/97 NAV 04 Handle daylight savings time problem - Btime6*/
/* 09/04/97 NAV 03 Add Btime6 Conversion routines */
/* 08/01/96 NAV 02 Lint CleanUp */
/* 11/08/94 MDE 01 New */
/************************************************************************/
#if defined (_WIN32)
#pragma warning(disable : 4996)
#endif
#include "glbtypes.h"
#include "sysincs.h"
#include "slog.h"
#include "time_str.h"
#include "str_util.h"
#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif
/************************************************************************/
ST_CHAR *tstrTimeFormat = TSTR_DEF_TIME_FORMAT;
ST_CHAR *BtimeTimeFormat = BTIME_DEF_TIME_FORMAT;
/************************************************************************/
/************************************************************************/
/* tstrTimeToString */
/************************************************************************/
ST_RET tstrTimeToString (time_t t, ST_CHAR *dest)
{
strftime (dest, MAX_TIME_STRING_LEN, tstrTimeFormat, localtime (&t));
return (SD_SUCCESS);
}
/************************************************************************/
/* tstrTimeToStringGmt */
/* this code was merged from time_str2.c */
/************************************************************************/
ST_RET tstrTimeToStringGmt (time_t t, ST_CHAR *dest)
{
strftime (dest, MAX_TIME_STRING_LEN, tstrTimeFormat, gmtime (&t));
return (SD_SUCCESS);
}
/************************************************************************/
/************************************************************************/
/* structure to hold results from parsing date and time strings */
typedef struct
{
int month;
int day;
int year;
int hour;
int min;
int sec;
int mSec;
int uSec;
int nSec;
int zoneHour;
int zoneMin;
char zoneCode; /* 'Z', '+', '-' or 0 */
int dateFound; /* not needed by new parsers */
int timeFound; /* not needed by new parsers */
int order; /*date-field ordering; 0 =f ANY */
char *pflags; /* ptr to (qual= ... ) or NULL */
} _TS_DATETIME;
#if 0
/* obsoleted code */
static int parseDateString (char *s, _TS_DATETIME *dt);
static int parseTimeString (char *s, _TS_DATETIME *dt);
static int parseBtimeString (char *s, _TS_DATETIME *dt);
static int strToMonth (char *s, int *monthOut);
#endif
/************************************************************************/
typedef struct
{
ST_INT32 /*O*/ len; /* length of field */
ST_INT32 /*O*/ value; /* extracted num value */
ST_INT32 /*O*/ scale; /* for scaling fractions */
ST_CHAR /*O*/ delim; /* field delimiter */
ST_CHAR /*O*/ text[16]; /* extracted text value */
} DATETIME_FIELD;
/************************************************************************/
/* getDateTimeField */
/* extract a (possible) date or time field, and report its value */
/* return pointer to next position to parse, or NULL if no field found */
/* a _DATETIME_FIELD len of 0 also means the field is not valid */
/************************************************************************/
static ST_CHAR * getDateTimeField (
ST_CHAR * /*I*/ buf, /* field being parsed */
ST_CHAR /*I*/ prevDelim, /* '.' allows longer num */
DATETIME_FIELD * /*O*/ dtf)
{
ST_INT32 i;
ST_CHAR work[4];
static ST_CHAR * monthTab[13] =
{
"",
"JAN", "FEB", "MAR",
"APR", "MAY", "JUN",
"JUL", "AUG", "SEP",
"OCT", "NOV", "DEC"
};
if (dtf == NULL)
{
return NULL; /* bad parameter */
}
dtf->len = 0;
dtf->value = 0;
dtf->scale = 1;
dtf->delim = ' ';
dtf->text[0] = 0;
if (buf == NULL)
{
return NULL; /* bad buf pointer */
}
/* skip leading whitespace */
while (isspace (*buf))
{
buf++;
}
if (*buf == 0)
{
return NULL; /* buf has no data */
}
/* accumulate value */
while (isdigit (*buf))
{
dtf->text[dtf->len] = *buf;
dtf->text[dtf->len+1] = 0;
dtf->value = (dtf->value * 10) + (*buf - '0');
dtf->scale *= 10;
dtf->len++;
buf++;
if (dtf->len > 9)
{
return NULL; /* numeric field too long */
}
} /* while */
if (dtf->len > 0) /* field is numeric */
{
/* only lengths 1, 2 and 4 are allowed */
/* this allows for 1 or 2 digit months, days and years */
/* and 4 digit years */
/* when prevDelim is '.', the current field is a fraction */
/* so, we allow longer field values */
if (prevDelim == '.')
{
if (dtf->len == 9)
{
/* a (fake) length of 9 is a 'marker' for an alpha month */
/* we change numbers of length 9 to 8 to get around this */
/* we are lying about the length; don't worry, it's OK */
/* the precise length of fractional seconds isn't critical anyway. */
/* we need lengths mainly to analyze month/day/year ordering. */
dtf->len = 8;
}
else if (dtf->len > 9)
{
/* too many digits for a fraction */
return NULL;
}
}
else /* prevDelim != '.' */
{
/* numbers for dates and times only have valid lengths of 1, 2 or 4 */
/* except for fractional seconds, which can vary from 0 to 9 digits */
if ( (dtf->len > 4)
|| (dtf->len == 3) )
{
/* not a valid date or time numeric field size */
return NULL;
}
/* if a number is followed by . then a blank, nul, +/-, T or Z */
/* the . is just noise, so ignore it */
/* this test is not made on the "if prevDelim == '.'" branch above */
/* we don't want to permit values like .123. which is illegal */
if (*buf == '.')
{
if ( (buf[1] == 0 )
|| (buf[1] == ' ')
|| (buf[1] == '+')
|| (buf[1] == '-')
|| (buf[1] == 'T')
|| (buf[1] == 'Z') )
{
buf++; /* a pointless dot */
}
} /* *buf == '.' */
} /* when prevDelim != '.' */
/* next field is after curr delim at 'buf'; assume delim len is 1 */
/* assume curr pos is the delimiter */
/* example: buf == "-12", so delim == '-' and next field "12" at buf+1 */
/* this holds in most cases, except AM/PM, when next field at buf+2 */
dtf->delim = (ST_CHAR) toupper (*buf);
if (*buf == 0) /* end of buffer */
{
/* delim is blank when there is no non-blank delimiter */
/* to determine if end of buffer, look at dtf->buf */
/* we don't return buf+1 because end of buffer was reached */
/* be careful not to run off end of buffer */
dtf->delim = ' ';
return buf;
}
if (isspace (*buf))
{
/* delim is blank when there is no non-blank delimiter */
/* to determine if end of buffer, look at dtf->buf */
dtf->delim = ' '; /* just in case of tabs, etc. */
return buf + 1;
}
if ( (*buf == ':') /* part of a time */
|| (*buf == '.') /* part of a time */
|| (*buf == '-') /* part of a date */
|| (*buf == '+') ) /* possibly part of a timezone offset */
{
return buf + 1;
}
if (*buf == '/')
{
dtf->delim = '-'; /* treat '/' same as '-' for dates */
return buf + 1;
}
if ( (dtf->delim == 'T')
&& (isdigit (buf[1])) ) /* value is part of ISO 8601 time */
{
return buf + 1;
}
if (dtf->delim == 'Z')
{
if ( (buf[1] <= ' ') /* Zulu/GMT timezone suffix */
|| (buf[1] == '(') ) /* Z followed by (qual= ... ) */
{
return buf + 1;
}
} /* Z */
/* look for A, P, AM or PM suffix */
if ( (dtf->delim == 'A')
|| (dtf->delim == 'P') )
{
if (!isalnum (buf[1])) /* short AM/PM suffix */
{
return buf + 1;
}
if ( (toupper (buf[1]) == 'M') /* long AM/PM suffix */
&& (!isalnum (buf[2])) ) /* good delim after M */
{
return buf + 2;
}
return NULL; /* malformed AM/PM */
} /* A or P delim */
} /* field is numeric */
/* look for alphabetic month */
/* we are supporting the following date formats: */
/* 2006-Dec-17 */
/* Dec-17-06 */
/* Dec-17-2006 */
/* 17-Dec-2006 */
/* in each case, the alpha month is followed by '-' or '/' */
/* the format 06-Dec-17 is not supported, because it is not */
/* clear if it is day-month-year or year-month-day order. */
/* if found, return equivalent value (1 to 12) and a (fake) length of 9 */
/* check for length of 9 is used to see if an alpha month is present */
if ( (buf[3] != '-')
&& (buf[3] != '/') )
{
return NULL;
}
work[0] = (ST_CHAR) toupper (buf[0]);
work[1] = (ST_CHAR) toupper (buf[1]);
work[2] = (ST_CHAR) toupper (buf[2]);
work[3] = 0;
strcpy (dtf->text, work);
for (i=1; i <= 12; i++)
{
if (strcmp (work, monthTab[i]) == 0)
{
dtf->delim = '-'; /* treat '/' same as '-' for dates */
dtf->value = i;
dtf->len = 9; /* fake length of 9 = alpha month */
return buf + 4;
}
}
return NULL; /* no valid date/time field found */
} /* getDateTimeField */
/************************************************************************/
/* getDateOrder */
/* */
/* on Windows systems, use GetLocaleInfo to determine the ordering of */
/* month, day and year fields in a date, according to the current */
/* locale. on non-Windows systems, assume m/d/y. */
/* */
/************************************************************************/
#ifdef _WIN32
/* on Windows, GetLocaleInfo() depends on windows.h and kernel32.lib */
#pragma comment(lib, "kernel32.lib")
#include <windows.h>
#endif
ST_INT32 getDateOrder ()
{
#ifdef _WIN32
ST_CHAR work[2];
ST_INT32 rc;
work[0] = work[1] = 0;
rc = GetLocaleInfoA (LOCALE_USER_DEFAULT, LOCALE_ILDATE, work, 2);
if (rc != 2)
{
return S_DATE_ORDER_MDY;
}
if (work[0] == '1')
{
return S_DATE_ORDER_DMY;
}
if (work[0] == '2')
{
return S_DATE_ORDER_YMD;
}
/* in case of error, or default '0' returned */
return S_DATE_ORDER_MDY;
#else
/* unable to determine ordering, so assume a default order */
return S_DATE_ORDER_MDY;
#endif
}
/************************************************************************/
/* storeDateValue */
/* */
/* take a DATETIME_FIELD array, determine field order of a date, and */
/* store into a _TS_DATETIME. if DATETIME_FIELD array is valid, and */
/* the fields appear to be in the correct order, return SD_SUCCESS, */
/* else return SD_FAILURE. */
/* */
/************************************************************************/
ST_RET storeDateValue (
DATETIME_FIELD * /*I*/ dtf,
_TS_DATETIME * /*IO*/ dt)
{
ST_INT32 len;
ST_INT32 mm;
ST_INT32 dd;
ST_INT32 yy;
ST_INT32 order;
if (dt == NULL)
{
return SD_FAILURE;
}
dt->month = 0;
dt->day = 0;
dt->year = 0;
dt->dateFound = SD_FALSE;
if (dtf == NULL)
{
return SD_FAILURE;
}
if ( (dtf[0].delim != '-')
|| (dtf[1].delim != '-') )
{
return SD_FAILURE;
}
if ( (dtf[2].delim != 'T')
&& (dtf[2].delim != ' ') )
{
return SD_FAILURE;
}
/* determine field order by the lengths of 3 fields */
/* form a single int value for easier testing */
/* for example, 412 means a 4-digit field, then a 1-digit field */
/* and finally a 2-digit field, in that order. */
len = (100 * dtf[0].len) + (10 * dtf[1].len) + (dtf[2].len);
/* if a specific date order is requested by caller, use it. */
/* otherwise, ask system for the default order */
if (dt->order == S_DATE_ORDER_ANY)
{
/* caller accepts any currently active order, based on locale */
order = getDateOrder ();
}
else
{
/* caller insisted on date fields being in a particular order */
order = dt->order;
}
switch (len)
{
case 411: /* yyyy-m-d : ordering is confident */
case 412: /* yyyy-m-dd : ordering is confident */
case 421: /* yyyy-mm-d : ordering is confident */
case 422: /* yyyy-mm-dd : ordering is confident */
case 491: /* yyyy-Mon-d : ordering is confident */
case 492: /* yyyy-Mon-dd : ordering is confident */
/* when date starts with a 4-digit number, the first field is a year. */
/* no (known) locales use year-day-month, so we can assume the order */
/* is year-month-day here, regardless of locale, with confidence. */
yy = dtf[0].value;
mm = dtf[1].value;
dd = dtf[2].value;
break;
case 914: /* Mon-d-yyyy : ordering is certain */
case 924: /* Mon-dd-yyyy : ordering is certain */
mm = dtf[0].value;
dd = dtf[1].value;
yy = dtf[2].value;
break;
case 114: /* m-d-yyyy or d-m-yyyy */
case 124: /* m-dd-yyyy or d-mm-yyyy */
case 214: /* mm-d-yyyy or dd-m-yyyy */
case 224: /* mm-dd-yyyy or dd-mm-yyyy */
if (order == S_DATE_ORDER_DMY)
{
dd = dtf[0].value;
mm = dtf[1].value;
}
else
{
/* for S_DATE_ORDER_MDY, the field ordering is certain */
/* for S_DATE_ORDER_YMD, the field ordering is not certain */
/* but since YMD implies month before year, we assume this is right */
mm = dtf[0].value;
dd = dtf[1].value;
}
yy = dtf[2].value;
break;
case 194: /* d-Mon-yyyy : for alpha month, ordering is certain */
case 294: /* dd-Mon-yyyy : for alpha month, ordering is certain */
dd = dtf[0].value;
mm = dtf[1].value;
yy = dtf[2].value;
break;
case 911: /* Mon-d-y : ordering is confident */
case 921: /* Mon-dd-y : ordering is confident */
case 912: /* Mon-d-yy : ordering is confident */
case 922: /* Mon-dd-yy : ordering is confident */
/* for alpha month, there is no such format as Mon-yy-dd */
/* so, the mm-dd-yy ordering is reasonably certain */
mm = dtf[0].value;
dd = dtf[1].value;
yy = dtf[2].value;
/* year is 2-digit, so form the default century */
/* we would normally have a cutoff of 1970, but in some cases */
/* a conversion error will leave a time_t with Dec 31 1969 */
/* so, values < 69 are assumed to be in the 21 century */
if (yy < 69)
{
yy += 2000;
}
else
{
yy += 1900;
}
break;
case 191: /* d-Mon-y */
case 291: /* dd-Mon-y */
case 192: /* d-Mon-yy */
case 292: /* dd-Mon-yy */
if (order == S_DATE_ORDER_YMD)
{
yy = dtf[0].value;
mm = dtf[1].value;
dd = dtf[2].value;
}
else
{
/* for S_DATE_ORDER_DMY, the field ordering is certain */
/* for S_DATE_ORDER_MDY, the field ordering is not certain */
/* but since MDY implies day before year, we assume this is right */
dd = dtf[0].value;
mm = dtf[1].value;
yy = dtf[2].value;
}
/* year is 2-digit, so form the default century */
/* we would normally have a cutoff of 1970, but in some cases */
/* a conversion error will leave a time_t with Dec 31 1969 */
/* so, values < 69 are assumed to be in the 21 century */
if (yy < 69)
{
yy += 2000;
}
else
{
yy += 1900;
}
break;
case 111: /* d-m-y y-m-d m-d-y */
case 112: /* d-m-yy y-m-dd m-d-yy */
case 121: /* d-mm-y y-mm-d m-dd-y */
case 122: /* d-mm-yy y-mm-dd m-dd-yy */
case 211: /* dd-m-y yy-m-d mm-d-y */
case 212: /* dd-m-yy yy-m-dd mm-d-yy */
case 221: /* dd-mm-y yy-mm-d mm-dd-y */
case 222: /* dd-mm-yy yy-mm-dd mm-dd-yy */
if (order == S_DATE_ORDER_DMY)
{
dd = dtf[0].value;
mm = dtf[1].value;
yy = dtf[2].value;
}
else if (order == S_DATE_ORDER_YMD)
{
yy = dtf[0].value;
mm = dtf[1].value;
dd = dtf[2].value;
}
else /* assume mm-dd-yy */
{
mm = dtf[0].value;
dd = dtf[1].value;
yy = dtf[2].value;
}
/* year is 2-digit, so form the default century */
/* we would normally have a cutoff of 1970, but in some cases */
/* a conversion error will leave a time_t with Dec 31 1969 */
/* so, values < 69 are assumed to be in the 21 century */
if (yy < 69)
{
yy += 2000;
}
else
{
yy += 1900;
}
break;
default:
return SD_FAILURE;
} /* switch */
if ( (mm < 1)
|| (mm > 12)
|| (dd < 1)
|| (dd > 31)
|| (yy < 1969) )
{
return SD_FAILURE;
}
dt->month = mm;
dt->day = dd;
dt->year = yy;
dt->dateFound = SD_TRUE;
return SD_SUCCESS;
} /* storeDateValue */
/************************************************************************/
/* storeTimeValue */
/* */
/* take a DATETIME_FIELD array, determine field order of a time, and */
/* store into a _TS_DATETIME. if DATETIME_FIELD array is valid, and */
/* the fields appear to be in the correct order, return SD_SUCCESS, */
/* else return SD_FAILURE. */
/* */
/************************************************************************/
ST_RET storeTimeValue (
DATETIME_FIELD * /*I*/ dtf,
_TS_DATETIME * /*O*/ dt)
{
ST_INT32 n = 0;
ST_INT32 len;
ST_INT32 hour = 0;
ST_INT32 min = 0;
ST_INT32 zoneHour = 0;
ST_INT32 zoneMin = 0;
ST_INT32 sec = 0;
ST_INT32 mSec = 0;
ST_INT32 uSec = 0;
ST_INT32 nSec = 0;
ST_CHAR work[32];
if (dt == NULL)
{
return SD_FAILURE;
}
dt->hour = 0;
dt->min = 0;
dt->sec = 0;
dt->mSec = 0;
dt->uSec = 0;
dt->nSec = 0;
dt->timeFound = SD_FALSE;
dt->zoneHour = 0;
dt->zoneMin = 0;
dt->zoneCode = 0;
if (dtf == NULL)
{
return SD_FAILURE;
}
/* hh:mm:ss[A] [.frac] [+hh:mm] yyyy-mm-dd */
/* 0 1 2 3 4 5 6 7 8 */
if ( (dtf[n].delim != ':')
&& (dtf[n].len != 1)
&& (dtf[n].len != 2) )
{
return SD_FAILURE;
}
hour = dtf[n++].value;
/* store minutes, a required field */
if ( (dtf[n].len != 1)
&& (dtf[n].len != 2) )
{
return SD_FAILURE;
}
min = dtf[n].value;
/* see if seconds field is present */
if (dtf[n].delim == ':')
{
n++; /* look at seconds field */
if ( (dtf[n].len != 1)
&& (dtf[n].len != 2) )
{
return SD_FAILURE;
}
sec = dtf[n].value;
}
/* see if AM/PM field is present, and validate hour */
/* in AM/PM mode, hour cannot be 00 or > 12 */
/* so 00:00 AM and 00:00 PM are illegal */
/* as are 14:00 PM etc. */
if (dtf[n].delim == 'A')
{
if ((hour < 1) || (hour > 12))
{
return SD_FAILURE;
}
if (hour == 12)
{
hour = 0; /* 12:05 AM is really 00:00 */
}
}
else if (dtf[n].delim == 'P')
{
if ((hour < 1) || (hour > 12))
{
return SD_FAILURE;
}
/* 12:05 PM is 12:05 (so hour == 12 is OK), but 1:05 PM is 13:05 */
if (hour < 12)
{
hour += 12;
}
}
/* if decimal point is followed by nothing, it is a null fraction */
/* if so, treat this as the end of the string */
/* for example, "12:34." is the same as "12:34" */
if ( (dtf[n].delim == '.')
&& (dtf[n+1].len == 0) )
{
dtf[n].delim = ' '; /* end of string */
}
if (dtf[n].delim == '.')
{
/* extract mSec */
/* this requires normalizing the value to 3 digits */
/* since the current field ends with a dot, there must be */
/* a following field that is numeric */
n++; /* look at fraction field */
len = dtf[n].len;
if (len > 9)
{
return SD_FAILURE; /* malformed/missing mSec field */
}
/* express fraction as a count of milliseconds */
/* method: pad or truncate ms field to 3 digits */
strncpy (work, dtf[n].text, 3);
work[3] = 0;
strcat (work, "000");
work[3] = 0;
mSec = atoi (work);
/* express fraction as a count of microseconds */
/* method: pad or truncate ms field to 6 digits */
strncpy (work, dtf[n].text, 6);
work[6] = 0;
strcat (work, "000000");
work[6] = 0;
uSec = atoi (work);
/* express fraction as a count of nanoseconds */
/* method: pad or truncate ms field to 9 digits */
strncpy (work, dtf[n].text, 9);
work[9] = 0;
strcat (work, "000000000");
work[9] = 0;
nSec = atoi (work);
}
if (dtf[n].delim == 'Z')
{
dt->zoneCode = 'Z';
}
else if ( (dtf[n].delim == '+') /* +hh:mm or +h:mm */
|| (dtf[n].delim == '-') ) /* -hh:mm or -h:mm */
{
dt->zoneCode = dtf[n].delim;
/* look at zone hour, it may be 1 or 2 digits */
n++;
/* allow for zone of +hhmm or -hhmm if it's the last field */
if ( (dtf[n].delim == ' ')
|| (dtf[n].delim == 'T') )
{
if (dtf[n].len != 4)
{
return SD_FAILURE; /* in this format, exactly 4 digits needed */
}
zoneHour = (dtf[n].value) / 100;
zoneMin = (dtf[n].value) % 100;
}
else
{
if ( (dtf[n].delim != ':') /* zone hour must have minutes */
&& (dtf[n].len != 1)
&& (dtf[n].len != 2) )
{
return SD_FAILURE;
}
zoneHour = dtf[n++].value; /* grab hours, index to minutes */
if (dtf[n].len != 2)
{
return SD_FAILURE;
}
zoneMin = dtf[n].value;
if ( (zoneHour < 0)
|| (zoneHour > 23)
|| (zoneMin < 0)
|| (zoneMin > 59) )
{
return SD_FAILURE;
}
}
}
if ( (hour < 0)
|| (hour > 23)
|| (min < 0)
|| (min > 59)
|| (sec < 0)
|| (sec > 59) )
{
return SD_FAILURE;
}
dt->hour = hour;
dt->min = min;
dt->sec = sec;
dt->mSec = mSec;
dt->uSec = uSec;
dt->nSec = nSec;
dt->timeFound = SD_TRUE;
dt->zoneHour = zoneHour;
dt->zoneMin = zoneMin;
return SD_SUCCESS;
} /* storeTimeValue */
/************************************************************************/
/* getTsDateTime */
/* parse a string containing a time and date value, and produce a */
/* struct tm value. accepted formats are: */
/* */
/* time [fraction] date */
/* date time [fraction] */
/* */
/* date may be in the following formats: */
/* */
/* yyyy-mm-dd */
/* yy-mm-dd (may be used in some locales) */
/* mm-dd-yy (US format) */
/* mm-dd-yyyy (US format) */
/* dd-mm-yy (European format) */
/* dd-mm-yyyy (European format) */
/* */
/* yyyy-Mon-dd */
/* Mon-dd-yy */
/* Mon-dd-yyyy */
/* dd-Mon-yyyy (European format) */
/* */
/* when date format may be ambiguous, the system is queried as to the */
/* default field ordering, based on the current locale. for example, */
/* 01-02-03 could be Jan 01 2003, 01 Feb 2003 or 2001 Feb 03. */
/* */
/* when date contains one 4-digit value, it is assumed to be a year. */
/* this helps to unambiguate some values. */
/* */
/* date delimiter may be '-' or '/' */
/* */
/* time may or may not have a fraction and/or a timezone */
/* */
/* max number of fields possible: 9, consisting of: */
/* date: 3 */
/* time: 3 */
/* fraction: 1 */
/* timezone: 2 */
/* */
/************************************************************************/
static ST_RET getTsDateTime (
ST_CHAR * /*I*/ in_buf,
_TS_DATETIME * /*IO*/ out_dt)
{
ST_INT32 maxfield = 10;
ST_INT32 numfields = 0;
ST_INT32 i;
ST_CHAR * buf;
ST_CHAR prevDelim = ' ';
ST_INT32 datefield;
ST_INT32 timefield;
DATETIME_FIELD dtf[10] = {{0}};
_TS_DATETIME dt = {0};
if ((in_buf == NULL) || (out_dt == NULL))
{
return SD_FAILURE; /* bad parameters */
}
dt.order = out_dt->order; /* copy date-ordering option */
/* extract fields */
/* prevDelim is set so that a '.' will allow the next field */
/* to have 1 to 7 digits */
buf = in_buf;
for (i=0; i < maxfield; i++)
{
buf = getDateTimeField (buf, prevDelim, &dtf[i]);
if (buf != NULL)
{
prevDelim = dtf[i].delim;
numfields++;
if (*buf == '(')
{
dt.pflags = buf; /* start of quality-flag field */
break;
}
}
} /* for */
if (numfields < 5)
{
return SD_FAILURE; /* we were expecting year,mon,day,hour,min fields */
}
/* determine field order */
if ( (dtf[0].delim == '-')
&& (dtf[1].delim == '-') )
{
/* first field is date */
/* example field layout: */
/* yyyy-mm-dd [T] hh:mm:ss[A] [.frac] [+hh:mm] */
/* 0 1 2 3 4 5 6 7 8 */
if ( (dtf[2].delim != ' ')
&& (dtf[2].delim != 'T') )
{
return SD_FAILURE; /* bad delimiter */
}
datefield = 0;
timefield = 3;
} /* if - - found */
else if (dtf[0].delim == ':')
{
/* first field is time (T code would not be present) */
/* example field layout: */
/* hh:mm:ss[A] [.frac] [Z|+hh:mm|-hh:mm] yyyy-mm-dd */
/* 0 1 2 3 4 5 6 7 8 */
/* ensure we have a valid time format */
/* mm must be followed by AM/PM code, :ss or space, not a '-' */
if ( (dtf[1].delim != ' ')
&& (dtf[1].delim != ':')
&& (dtf[1].delim != 'Z')
&& (dtf[1].delim != '+')
&& (dtf[1].delim != '-')
&& (dtf[1].delim != 'A')
&& (dtf[1].delim != 'P') )
{
return SD_FAILURE; /* bad delimiter */
}
timefield = 0;
datefield = 0;
/* find date field by looking for two '-' delimiters */
/* if numfields == 9, then last field is at [8] */
/* so we look for pairs up to [numfields-1] */
for (i = 2; i < numfields-1; i++)
{
if ( (dtf[i].delim == '-')
&& (dtf[i+1].delim == '-') )
{
datefield = i;
break;
}
} /* for */
if (datefield == 0)
{
return SD_FAILURE; /* cannot find date field */
}
} /* if : found */
else
{
return SD_FAILURE; /* cannot determine date-time vs. time-date */
}
/* populate dt structure with date and time values */
if (storeDateValue (&dtf[datefield], &dt) != SD_SUCCESS)
{
return SD_FAILURE; /* malformed date */
}
if (storeTimeValue (&dtf[timefield], &dt) != SD_SUCCESS)
{
return SD_FAILURE; /* malformed time */
}
*out_dt = dt;
return SD_SUCCESS;
} /* getTsDateTime */
/************************************************************************/
/* tstrStringToTm */
/* parse a string containing a time and date value, and produce a */
/* struct tm value. method: convert to _TS_DATEITIME via getTsDateTime */
/* then convert result to struct tm. */
/************************************************************************/
ST_RET tstrStringToTm (ST_CHAR *in_buf, struct tm *out_struct_tm)
{
_TS_DATETIME dt = {0};
struct tm w_struct_tm = {0};
if ((in_buf == NULL) || (out_struct_tm == NULL))
{
return SD_FAILURE; /* bad parameters */
}
/* compatibility with old time_str.c requires month-day-year ordering */
dt.order = S_DATE_ORDER_MDY;
if (getTsDateTime (in_buf, &dt) != SD_SUCCESS)
{
return SD_FAILURE; /* bad parameters */
}
/* convert the components to struct tm */
w_struct_tm.tm_year = dt.year - 1900;
w_struct_tm.tm_mon = dt.month - 1;
w_struct_tm.tm_mday = dt.day;
w_struct_tm.tm_hour = dt.hour;
w_struct_tm.tm_min = dt.min;
w_struct_tm.tm_sec = dt.sec;
w_struct_tm.tm_isdst = -1; /* let api determine DST */
usr_mkgmtime (&w_struct_tm); /* generate tm_wday, tm_yday */
/* we do not know if supplied date was GMT or not, so DST is uncertain */
w_struct_tm.tm_isdst = -1; /* a reasonable default value */
*out_struct_tm = w_struct_tm;
return SD_SUCCESS;
} /* tstrStringToTm */
/************************************************************************/
/* tstrStringToTime */
/* parse a string containing a time and date value, and produce a */
/* time_t value. method: convert to struct tm via tstrStringToTm, */
/* then convert result to time_t. */
/************************************************************************/
ST_RET tstrStringToTime (ST_CHAR *in_buf, time_t *out_time_t)
{
time_t w_time_t;
struct tm w_struct_tm;
if ((in_buf == NULL) || (out_time_t == NULL))
{
return SD_FAILURE; /* bad parameters */
}
if (tstrStringToTm (in_buf, &w_struct_tm) != SD_SUCCESS)
{
return SD_FAILURE; /* parse failed */
}
w_time_t = mktime (&w_struct_tm);
if (w_time_t == (time_t) -1)
{
return SD_FAILURE;
}
*out_time_t = w_time_t;
return SD_SUCCESS;
} /* tstrStringToTime */
/************************************************************************/
/* tstrStringToTimeGmt */
/* time_t value. method: convert to struct tm via tstrStringToTm, */
/* then convert result to time_t using usr_mkgmtime(). */
/************************************************************************/
ST_RET tstrStringToTimeGmt (ST_CHAR *in_buf, time_t *out_time_t)
{
time_t w_time_t;
struct tm w_struct_tm;
if ((in_buf == NULL) || (out_time_t == NULL))
{
return SD_FAILURE; /* bad parameters */
}
if (tstrStringToTm (in_buf, &w_struct_tm) != SD_SUCCESS)
{
return SD_FAILURE; /* parse failed */
}
w_time_t = usr_mkgmtime (&w_struct_tm);
if (w_time_t == (time_t) -1)
{
return SD_FAILURE;
}
*out_time_t = w_time_t;
return SD_SUCCESS;
} /* tstrStringToTimeGmt */
/************************************************************************/
/* tstrTmToString */
/* Convert struct tm to string. */
/************************************************************************/
ST_RET tstrTmToString (struct tm *t, ST_CHAR *dest)
{
ST_CHAR *timeFormatStr = "%m-%d-%Y %H:%M:%S";
strftime (dest, 30, timeFormatStr, t);
return (SD_SUCCESS);
}
#if 0
/* obsoleted code */
/************************************************************************/
/* parseDateString */
/************************************************************************/
/* parseDateString IS NO LONGER REFERENCED IN THIS CODE */
static ST_RET parseDateString (ST_CHAR *s, _TS_DATETIME *dt)
{
ST_CHAR *p;
ST_CHAR *d1;
ST_CHAR *d2;
ST_CHAR *d3;
int century;
dt->dateFound = SD_TRUE;
/* break into three substrings */
d1 = s;
p = strpbrk (s,"-");
*p = 0;
d2 = ++p;
p = strpbrk (d2,"-");
if (!p)
return (SD_FAILURE);
*p = 0;
d3 = ++p;
/* Now process each date sub-substring seperately */
/* Check for alpha month forms */
if (isalpha (*d1))
{
if (strToMonth (d1, &dt->month))
return (SD_FAILURE);
if (!sscanf (d2, "%d", &dt->day))
return (SD_FAILURE);
}
else if (isalpha (*d2))
{
if (!sscanf (d1, "%d", &dt->day))
return (SD_FAILURE);
if (strToMonth (d2, &dt->month))
return (SD_FAILURE);
}
else /* Not an ALPHA month form */
{
/* Numeric month is d1 */
if (!sscanf (d1, "%d", &dt->month))
return (SD_FAILURE);
--dt->month; /* we use 0-11 for month */
/* Numeric day is d2 */
if (!sscanf (d2, "%d", &dt->day))
return (SD_FAILURE);
}
/* Numeric year is always d3 */
if (!sscanf (d3, "%d", &dt->year))
return (SD_FAILURE);
if (strlen (d3) == 2)
{
/* any year < 1984 is assumed to be in the 21st century */
century = (dt->year < 84) ? 2000 : 1900;
dt->year += century;
}
return (SD_SUCCESS);
}
/* end of obsoleted code */
#endif
#if 0
/* obsoleted code */
/************************************************************************/
/* parseTimeString */
/************************************************************************/
/* parseTimeString IS NO LONGER REFERENCED IN THIS CODE */
static ST_RET parseTimeString (ST_CHAR *s, _TS_DATETIME *dt)
{
int pm;
ST_CHAR *p, *t1, *t2, *t3;
dt->timeFound = SD_TRUE;
/* break into two substrings */
p = strpbrk (s,":");
t1 = s; /* hours are here */
*p = 0;
t2 = ++p; /* minutes are here */
if (!p)
return (SD_FAILURE);
p = strpbrk (t2, ":");
if (!p)
t3 = NULL;
else
{
*p = 0;
t3 = ++p;
}
pm = SD_FALSE;
if (t3)
{
if (t3[2] == 'A' || t3[2] == 'a')
{
t3[2] = 0;
}
else if (t3[2] == 'P' || t3[2] == 'p')
{
pm = SD_TRUE;
t3[2] = 0;
}
}
if (!sscanf (t1, "%d", &dt->hour))
return (SD_FAILURE);
if (!sscanf (t2, "%d", &dt->min))
return (SD_FAILURE);
if (t3)
sscanf (t3, "%d", &dt->sec);
else
dt->sec = 0;
if (pm)
dt->hour += 12;
return (SD_SUCCESS);
}
/* end of obsoleted code */
#endif
/************************************************************************/
#if 0
/* obsoleted code */
static ST_CHAR *monthStrings[12] =
{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
static ST_RET strToMonth (ST_CHAR *s, int *monthOut)
{
int i;
for (i = 0; i < 12; ++i)
{
if (!stricmp (s, monthStrings[i]))
{
*monthOut = i;
return (SD_SUCCESS);
}
}
return (SD_FAILURE);
}
#endif
/************************************************************************/
/* Btime6 Conversion Routines: */
/************************************************************************/
#define SEC_PER_MIN 60
#define SEC_PER_HOUR (60 * SEC_PER_MIN)
#define SEC_PER_DAY (24 * SEC_PER_HOUR)
#define MSEC_PER_SEC 1000
/************************************************************************/
/* Btime6StringToVals: Receive string in BTIME_DEF_TIME_FORMAT+mSec */
/* and return the number of days and msecs */
/* since Jan 1, 1984 midnight */
/* */
/* format BTIME_DEF_TIME_FORMAT.mSec is 'mm-dd-yyyy hh:mm:ss.mmm' */
/* NOTE: output values are NOT adjusted for timezone or DST */
/* */
/* method: parse string, capturing mSec. create time_t, and adjust */
/* for difference in seconds between 1970-01-01 and 1984-01-01. */
/************************************************************************/
ST_RET Btime6StringToVals (ST_CHAR *src, ST_INT32 *numDays, ST_INT32 *numMSec)
{
_TS_DATETIME dt = {0};
struct tm w_struct_tm = {0};
time_t w_time_t;
time_t w_days;
time_t w_msec;
if ((src == NULL) || (numDays == NULL) || (numMSec == NULL))
{
return SD_FAILURE; /* bad parameters */
}
/* for Btime strings, month-day-year date ordering is required */
dt.order = S_DATE_ORDER_MDY;
if (getTsDateTime (src, &dt) != SD_SUCCESS)
{
SLOGALWAYS1 ("Btime6StringToVals: Unable to parse Btime6 '%s'", src);
return SD_FAILURE; /* bad parameters */
}
/* convert the components to struct tm */
w_struct_tm.tm_year = dt.year - 1900;
w_struct_tm.tm_mon = dt.month - 1;
w_struct_tm.tm_mday = dt.day;
w_struct_tm.tm_hour = dt.hour;
w_struct_tm.tm_min = dt.min;
w_struct_tm.tm_sec = dt.sec;
w_struct_tm.tm_isdst = 0; /* BTime is GMT based */
w_time_t = usr_mkgmtime (&w_struct_tm); /* portable GMT-based mktime() */
if (w_time_t == (time_t) -1)
{
SLOGALWAYS1 ("Btime6StringToVals: Unable to convert '%s' to time_t", src);
return SD_FAILURE;
}
/* adjust for diff between 1984 (Btime epoch) and 1970 (Unix time epoch) */
/* this is a known constant amount, so we don't recalculate it each time */
w_time_t -= (time_t) S_SECS_DIFF_1984_1970;
w_days = w_time_t / SEC_PER_DAY;
w_msec = (w_time_t % SEC_PER_DAY) * 1000; /* EJV CORR: the multiplication by 1000 was missing */
/* save the calculated time value */
*numDays = (ST_UINT32) w_days;
*numMSec = (ST_UINT32) w_msec + (ST_UINT32) dt.mSec;
return SD_SUCCESS;
} /* Btime6StringToVals */
/************************************************************************/
/* Btime6ValsToString: Receive the number of days and msecs since */
/* Jan 1, 1984 - midnight and return a string in */
/* BTIME_DEF_TIME_FORMAT+mSec */
/************************************************************************/
ST_RET Btime6ValsToString (char *dest, ST_INT32 numDays, ST_INT32 numMSec)
{
struct tm tmVal;
time_t tmRslt;
long numSeconds, balMSec;
char stash[MAX_TIME_STRING_LEN+1];
ldiv_t divResult;
_TS_DATETIME dt;
divResult = ldiv (numMSec, MSEC_PER_SEC);
numSeconds = divResult.quot;
balMSec = divResult.rem;
divResult = ldiv (numSeconds, SEC_PER_DAY);
dt.day = divResult.quot + numDays;
numSeconds = divResult.rem;
divResult = ldiv (numSeconds, SEC_PER_HOUR);
dt.hour = divResult.quot;
numSeconds = divResult.rem;
divResult = ldiv (numSeconds, SEC_PER_MIN);
dt.min = divResult.quot;
numSeconds = divResult.rem;
dt.sec = numSeconds;
/* set up the struct tm */
tmVal.tm_wday = 0; /* this is an output parameter */
tmVal.tm_yday = 0; /* this is an output parameter */
tmVal.tm_year = 84;
tmVal.tm_mon = 0;
tmVal.tm_mday = dt.day+1;
tmVal.tm_hour = dt.hour;
tmVal.tm_min = dt.min;
tmVal.tm_sec = dt.sec;
tmVal.tm_isdst = -1; /* let function guess */
tmRslt = mktime(&tmVal);
if (tmRslt == (time_t) -1)
return (SD_FAILURE);
/* now turn it into a string with out mSeconds */
strftime (stash, MAX_TIME_STRING_LEN, BtimeTimeFormat, localtime (&tmRslt));
/* now append the mSeconds to the string */
sprintf (dest, "%s.%03ld", stash, balMSec);
return (SD_SUCCESS);
} /* Btime6ValsToString */
/************************************************************************/
/* Btime4StringToVals: Receive string in BTIME_DEF_TIME_FORMAT.mSec */
/* and return the number of msecs since midnight */
/* format BTIME_DEF_TIME_FORMAT.mSec is 'mm-dd-yyyy hh:mm:ss.mmm' */
/************************************************************************/
ST_RET Btime4StringToVals (char *src, ST_INT32 *numMSec)
{
_TS_DATETIME dt = {0};
struct tm * p_struct_tm = {0};
time_t w_time_t;
ST_INT32 curDay;
ST_INT32 curMonth;
ST_INT32 curYear;
ST_INT32 totalMsec;
if ((src == NULL) || (numMSec == NULL))
{
return SD_FAILURE; /* bad parameters */
}
/* get current time for validation of date */
w_time_t = time (NULL);
if (w_time_t == (time_t) -1)
{
SLOGALWAYS0 ("Btime4StringToVals: Unable to obtain current time");
return SD_FAILURE; /* normally should not occur */
}
/* current time is converted to a GMT-based struct tm */
/* because the Btime6 version of this function calculates the offset */
/* from 1984-01-01 GMT, so we use a GMT-based struct tm here to be */
/* consistent. */
p_struct_tm = gmtime (&w_time_t);
if (p_struct_tm == NULL)
{
SLOGALWAYS0 (
"Btime4StringToVals: Unable to convert current time_t to struct tm");
return SD_FAILURE; /* normally should not occur */
}
curYear = p_struct_tm->tm_year + 1900;
curMonth = p_struct_tm->tm_mon + 1;
curDay = p_struct_tm->tm_mday;
/* for Btime strings, month-day-year date ordering is required */
dt.order = S_DATE_ORDER_MDY;
if (getTsDateTime (src, &dt) != SD_SUCCESS)
{
SLOGALWAYS1 ("Btime4StringToVals: Unable to parse Btime4 '%s'", src);
return SD_FAILURE; /* bad parameters */
}
if ( (dt.year != curYear )
|| (dt.month != curMonth)
|| (dt.day != curDay ) )
{
SLOGALWAYS0 ("Btime4 Conversion Error: Input date must be today's date");
return SD_FAILURE;
}
/* get mSecs from hours/min/secs */
totalMsec = (dt.hour * SEC_PER_HOUR * MSEC_PER_SEC)
+ (dt.min * SEC_PER_MIN * MSEC_PER_SEC)
+ (dt.sec * MSEC_PER_SEC)
+ (dt.mSec);
/* ensure mSecs does not exceed num mSecs in a day. the count must not */
/* equal ms/day either, otherwise it would be an offset to the next day */
if ( (totalMsec < 0)
|| (totalMsec >= (SEC_PER_DAY * MSEC_PER_SEC)) )
{
SLOGALWAYS1 ("Btime4 Conversion Error: Invalid millisecond count: %d", totalMsec);
return SD_FAILURE;
}
*numMSec = totalMsec;
return SD_SUCCESS;
} /* Btime4StringToVals */
/************************************************************************/
/* Btime4ValsToString: Receive number of msecs since midnight and return*/
/* string in BTIM_DEF_TIME_FORMAT.mSec */
/************************************************************************/
ST_RET Btime4ValsToString (char *dest, ST_INT32 numMSec)
{
int curDay, curMonth, curYear;
struct tm tmVal, *curTime;
time_t tmRslt;
long numSeconds, balMSec;
char stash[MAX_TIME_STRING_LEN+1];
ldiv_t divResult;
_TS_DATETIME dt;
time_t theTime;
/* figure out todays date */
theTime = time (NULL);
curTime = localtime (&theTime);
curDay = curTime->tm_mday;
curMonth = curTime->tm_mon;
curYear = curTime->tm_year;
divResult = ldiv (numMSec, MSEC_PER_SEC);
numSeconds = divResult.quot;
balMSec = divResult.rem;
divResult = ldiv (numSeconds, SEC_PER_HOUR);
dt.hour = divResult.quot;
numSeconds = divResult.rem;
divResult = ldiv (numSeconds, SEC_PER_MIN);
dt.min = divResult.quot;
numSeconds = divResult.rem;
dt.sec = numSeconds;
/* set up the struct tm */
tmVal.tm_wday = 0; /* this is an output parameter */
tmVal.tm_yday = 0; /* this is an output parameter */
tmVal.tm_year = curYear;
tmVal.tm_mon = curMonth;
tmVal.tm_mday = curDay;
tmVal.tm_hour = dt.hour;
tmVal.tm_min = dt.min;
tmVal.tm_sec = dt.sec;
tmVal.tm_isdst = -1; /* let function guess */
tmRslt = mktime(&tmVal);
if (tmRslt == (time_t) -1)
return (SD_FAILURE);
/* now turn it into a string with out mSeconds */
strftime (stash, MAX_TIME_STRING_LEN, BtimeTimeFormat, localtime (&tmRslt));
/* now append the mSeconds to the string */
sprintf (dest, "%s.%03ld", stash, balMSec);
return (SD_SUCCESS);
}
#if 0
/* obsoleted code */
/************************************************************************/
/* parseBtimeString: parse HH:MM:SS:msec */
/************************************************************************/
/* parseBtimeString IS NO LONGER REFERENCED IN THIS CODE */
static ST_RET parseBtimeString (char *s, _TS_DATETIME *dt)
{
char stash[MAX_TIME_STRING_LEN+1];
char toFind[] = ":.";
char *token;
int count = 0;
dt->timeFound = SD_TRUE;
/* make a copy first */
strcpy (stash, s);
/* set default values */
dt->hour = dt->min = dt->sec = dt->mSec = 0;
/* do the token thing to separate the string */
token = strtok (stash, toFind);
while (token != NULL)
{
switch (count)
{
case 0:
dt->hour = atoi (token);
break;
case 1:
dt->min = atoi (token);
break;
case 2:
dt->sec = atoi (token);
break;
case 3:
dt->mSec = atoi (token);
break;
}
count++;
/* get next token */
token = strtok (NULL, toFind);
}
return (SD_SUCCESS);
}
/* end of obsoleted code */
#endif
/************************************************************************/
/* validQualField */
/* verify that a string contains a valid Quality field (qual=b,b,b,n) */
/* there must be at least 3 bit flags present, and proper comma delims. */
/* if valid, return number characterizing the field, else return 0. */
/* then, create a buffer with the extracted flags and 'n' value, */
/* with default values if trailing flags or ''n' value is omitted. */
/************************************************************************/
static ST_INT32 validQualField (
ST_CHAR * qual,
ST_CHAR * flagBuf)
{
ST_CHAR work[32];
ST_INT32 i;
ST_INT32 digit = 0;
if ((qual == NULL) | (flagBuf == NULL))
{
return 0;
}
/* (qual=1,2,3,n) */
/* 0123456789012345 */
/* 111111 */
strcpy (flagBuf, "00000"); /* create defaults */
/* make copy of string, modifying it to create a pattern to verify */
for (i=0; i < 24; i++)
{
if (qual[i] <= ' ')
{
break;
}
else if (qual[i] == ')')
{
work[i++] = ')';
break;
}
else if (isdigit (qual[i]))
{
/* first 3 digits must be 0 or 1, rest is value 0 to 31 */
digit++;
if (digit <= 3)
{
if (qual[i] > '1')
{
return 0; /* char is digit but > '1', not a valid bit value */
}
work[i] = '1'; /* '1' stands for '0' or '1' */
}
else /* digits after 3rd */
{
work[i] = '9'; /* to check pattern */
}
} /* digit */
else
{
work[i] = (ST_CHAR) toupper (qual[i]);
}
} /* for */
work[i] = 0;
if (strcmp (work, "(QUAL=1,1,1)") == 0)
{
flagBuf[0] = qual[6];
flagBuf[1] = qual[8];
flagBuf[2] = qual[10];
return 3;
}
else if (strcmp (work, "(QUAL=1,1,1,9)") == 0)
{
flagBuf[0] = qual[6];
flagBuf[1] = qual[8];
flagBuf[2] = qual[10];
flagBuf[3] = qual[12]; /* 1-digit 'n' value */
flagBuf[4] = 0; /* shorten return value */
/* when 'n' value is 1-digit, any digit value is OK */
return 4;
}
else if (strcmp (work, "(QUAL=1,1,1,99)") == 0)
{
ST_INT32 num;
flagBuf[0] = qual[6];
flagBuf[1] = qual[8];
flagBuf[2] = qual[10];
flagBuf[3] = qual[12]; /* 2-digit 'n' value */
flagBuf[4] = qual[13];
/* when 'n' value is 1-digit, check for range 0 to 31 */
/* the 'n' value is supposed to be a 5-bit value */
num = atoi (flagBuf + 3);
if ((num < 0) || (num > 31))
{
return 0; /* 'n' value is out of range */
}
return 6;
}
/* quality field was none of the above formats */
return 0;
} /* validQualField */
/************************************************************************/
/* UTC Time Conversion Functions: */
/* Format = YYYY-MM-DDThh:mm:ss.000000000Z(qual=b,b,b,b,n) */
/************************************************************************/
/* UtcStringToVals: */
/************************************************************************/
ST_RET UtcStringToVals (
ST_CHAR * src,
ST_UINT32 * pSecs,
ST_UINT32 * pFraction,
ST_UINT32 * pQflags)
{
_TS_DATETIME dt = {0};
struct tm w_struct_tm = {0};
time_t w_time_t;
ST_CHAR * pflags;
ST_CHAR flagBuf[8];
ST_INT b0;
ST_INT b1;
ST_INT b2;
ST_INT bx;
ST_DOUBLE decSecs;
ST_DOUBLE binSecs;
if ( (src == NULL)
|| (pSecs == NULL)
|| (pFraction == NULL)
|| (pQflags == NULL) )
{
SLOGALWAYS0 ("UtcStringToVals: NULL parameters");
return SD_FAILURE; /* bad parameters */
}
/* for UTC time strings, date ordering is yyyy-mm-dd*/
dt.order = S_DATE_ORDER_YMD;
dt.pflags = NULL;
if (getTsDateTime (src, &dt) != SD_SUCCESS)
{
SLOGALWAYS1 ("UtcStringToVals: Unable to parse time '%s'", src);
return SD_FAILURE; /* bad parameters */
}
if (dt.zoneCode != 'Z')
{
SLOGALWAYS1 ("UtcStringToVals: GMT timezone code Z missing in '%s'", src);
return SD_FAILURE;
}
pflags = dt.pflags; /* pointer to (qual string */
if (pflags == NULL)
{
/* quality fields were not present, assume they are all 0 */
/* SLOGALWAYS1 ("UtcStringToVals: Missing quality in '%s'", src); */
*pQflags = 0;
}
else if (validQualField (pflags, flagBuf) < 3) /* not enough flags */
{
SLOGALWAYS1 ("UtcStringToVals: Invalid quality in '%s'", src);
return SD_FAILURE;
}
else
{
/* flags are already validated as '0' or '1' char values, so AND to get */
/* bit values, and shift into correct positions */
b0 = (flagBuf[0] & 1) << 7;
b1 = (flagBuf[1] & 1) << 6;
b2 = (flagBuf[2] & 1) << 5;
bx = atoi (flagBuf + 3);
*pQflags = (b0 | b1 | b2 | bx);
}
/* convert the components to struct tm */
w_struct_tm.tm_year = dt.year - 1900;
w_struct_tm.tm_mon = dt.month - 1;
w_struct_tm.tm_mday = dt.day;
w_struct_tm.tm_hour = dt.hour;
w_struct_tm.tm_min = dt.min;
w_struct_tm.tm_sec = dt.sec;
w_struct_tm.tm_isdst = 0;
w_time_t = usr_mkgmtime (&w_struct_tm); /* portable GMT-based mktime() */
if (w_time_t == (time_t) -1)
{
SLOGALWAYS1 ("UtcStringToVals: Unable to convert '%s' to time_t", src);
return SD_FAILURE;
}
/* save the calculated time value */
*pSecs = (ST_UINT32) w_time_t;
/* set the decimal fraction of seconds */
/* dt.nSec contains a count of nanoseconds */
/* this must be converted to a 24-bit binary fraction of fractional time */
/* note: 0x1000000 == 16777216 */
/* form decimal fraction of 1 second from 0.0 to 0.999,999,999 */
/* 123456789 */
decSecs = ((ST_DOUBLE) dt.nSec) / 1000000000.0;
/* convert to binary fraction of 1 second from 0x0.0 to 0x0.FFFFFF */
binSecs = (decSecs * 16777216.0) + 0.5;
/* return int equivalent count to caller */
*pFraction = (ST_UINT32) binSecs;
return SD_SUCCESS;
} /* UtcStringToVals */
/************************************************************************/
/* UtcValsToString: */
/************************************************************************/
ST_RET UtcValsToString (char *dest, ST_UINT32 secs, ST_UINT32 fraction,
ST_UINT32 qflags)
{
ST_CHAR theDate[MAX_TIME_STRING_LEN];
ST_CHAR theFraction[25];
ST_CHAR theQual[25];
ST_DOUBLE dFraction;
ST_CHAR *pFract;
ST_CHAR b0, b1, b2;
ST_INT rest;
time_t t = secs;
struct tm *pTm;
/* get the date portion */
pTm = gmtime (&t);
if (!pTm)
{
SLOGALWAYS0 ("UtcValsToString: conversion failure - invalid seconds.");
return SD_FAILURE;
}
strftime (theDate, MAX_TIME_STRING_LEN, UTC_DEF_TIME_FORMAT, pTm);
/* get the fraction portion */
dFraction = ((ST_DOUBLE) fraction / (ST_DOUBLE) 0x01000000);
sprintf (theFraction, " %#0.09f", dFraction);
pFract = strchr (theFraction, '.');
if (!pFract)
{
SLOGALWAYS1 ("UtcToString - unable to convert fraction %d", fraction);
return SD_FAILURE;
}
/* get the qflags */
b0 = b1 = b2 = rest ='0';
if (qflags & 0x80) b0 = '1';
if (qflags & 0x40) b1 = '1';
if (qflags & 0x20) b2 = '1';
rest = (qflags & 0x1F);
sprintf (theQual, "Z(qual=%c,%c,%c,%d)", b0, b1, b2, rest);
/* put them together */
sprintf (dest, "%s%s%s", theDate, pFract, theQual);
return SD_SUCCESS;
}
/************************************************************************/
/* XmlStringToUtcValue */
/************************************************************************/
/* An input time and date string is converted to the number of */
/* seconds since 1/1/1970. */
/* */
/* Any of the following strings are valid input to this subroutine: */
/* */
/* "yyyy-mm-ddThh:mm:ss.fffff+/-hh:mm" */
/* "yyyy-mm-ddThh:mm:ss+/-hh:mm" */
/* */
/* "yyyy-mm-ddThh:mm:ss.fffffZ" */
/* "yyyy-mm-ddThh:mm:ssZ" */
/* */
/* "yyyy-mm-ddThh:mm:ss.fffff" */
/* "yyyy-mm-ddThh:mm:ss" */
/* */
/* Note: */
/* Decimal fraction for microseconds: .fffff */
/* East time zone offset from GMT (Greenwich Mean Time): +hh:mm */
/* West time zone offset from GMT (Greenwich Mean Time): -hh:mm */
/* */
/* */
/* Output is stored in SX_DATE_TIME structure as: */
/* */
/* dateTime stored as number of seconds elapsed since */
/* midnight (00:00:00) January 1, 1970, */
/* UTC (Coordinated Universal Time), according */
/* to the system clock */
/* useMicroseconds indicates decimal fraction for seconds was */
/* specified */
/* microseconds specified decimal fraction of seconds stored as */
/* microseconds */
/* useTZ indicates a time zone offset is present */
/* tz time zone offset specified as minutes */
/* */
/* Time zone offset "tz" and time zone presence "useTZ" will be */
/* specified in output as follows: */
/* */
/* "yyyy-mm-ddThh:mm:ss.fffff+/-hh:mm" 'useTZ = SD_TRUE' */
/* 'tz = +/-seconds' */
/* "yyyy-mm-ddThh:mm:ss+/-hh:mm" 'useTZ = SD_TRUE' */
/* 'tz = +/-seconds' */
/* */
/* "yyyy-mm-ddThh:mm:ss.fffffZ" 'useTZ = SD_TRUE' 'tz = 0' */
/* "yyyy-mm-ddThh:mm:ssZ" 'useTZ = SD_TRUE' 'tz = 0' */
/* */
/* "yyyy-mm-ddThh:mm:ss.fffff" 'useTZ = SD_FALSE' 'tz ignored' */
/* "yyyy-mm-ddThh:mm:ss" 'useTZ = SD_FALSE' 'tz ignored' */
/* */
/************************************************************************/
ST_RET XmlStringToUtcValue (ST_CHAR *in_buf, SX_DATE_TIME *sxDateTime)
{
_TS_DATETIME dt = {0};
struct tm w_struct_tm = {0};
time_t w_time_t; /* local time in seconds */
if ((in_buf == NULL) || (sxDateTime == NULL))
{
SLOGALWAYS0 ("XmlStringToUtcValue: NULL parameters");
return SD_FAILURE; /* bad parameters */
}
/* split up the string input from XML file so we can store */
/* number of seconds, number of microseconds (decimal */
/* fraction of seconds) and number of minutes in time zone */
/* offset separately */
/* for XML time strings, date ordering is yyyy-mm-dd*/
dt.order = S_DATE_ORDER_YMD;
if (getTsDateTime (in_buf, &dt) != SD_SUCCESS)
{
return SD_FAILURE; /* bad parameters */
}
/* convert the components to struct tm */
w_struct_tm.tm_year = dt.year - 1900;
w_struct_tm.tm_mon = dt.month - 1;
w_struct_tm.tm_mday = dt.day;
w_struct_tm.tm_hour = dt.hour;
w_struct_tm.tm_min = dt.min;
w_struct_tm.tm_sec = dt.sec;
/* set the decimal fraction of seconds if present */
if (dt.uSec == 0)
{
sxDateTime->microseconds = 0;
sxDateTime->useMicroseconds = SD_FALSE;
}
else
{
sxDateTime->microseconds = dt.uSec;
sxDateTime->useMicroseconds = SD_TRUE;
}
/* get the time zone offset in minutes if present */
if (dt.zoneCode) /* '+', '-' or 'Z' present */
{
sxDateTime->useTZ = SD_TRUE;
sxDateTime->tz = (dt.zoneHour * 60) + dt.zoneMin;
if (dt.zoneCode == '-')
{
sxDateTime->tz = -sxDateTime->tz;
}
}
else
{
sxDateTime->useTZ = SD_FALSE;
sxDateTime->tz = 0;
}
/* calculate total seconds in UTC time so we can store it */
/* as a number of seconds since 01/01/1970 */
if (sxDateTime->useTZ && sxDateTime->tz == 0)
{
w_struct_tm.tm_isdst = 0;
w_time_t = usr_mkgmtime (&w_struct_tm); /* portable GMT-based mktime() */
}
else
{
w_struct_tm.tm_isdst = -1;
w_time_t = mktime (&w_struct_tm);
}
if (w_time_t == (time_t) -1)
{
SLOGALWAYS1 ("XmlStringToUtcValue: Unable to convert time '%s'", in_buf);
return SD_FAILURE;
}
/* save the calculated time value */
sxDateTime->dateTime = w_time_t;
return SD_SUCCESS;
} /* XmlStringToUtcValue */
/************************************************************************/
/* UtcValueToXmlString */
/************************************************************************/
/* The specified number of seconds since 1/1/1970 is converted to a */
/* time and date string. */
/* */
/* Input is stored in "SX_DATE_TIME" as: */
/* */
/* dateTime stored as number of seconds elapsed since */
/* midnight (00:00:00) January 1, 1970, */
/* UTC (Coordinated Universal Time), according */
/* to the system clock */
/* useMicroseconds indicates decimal fraction of seconds was */
/* specified */
/* microseconds decimal fraction of seconds specified stored as */
/* microseconds */
/* useTZ indicates a time zone offset is present */
/* tz time zone offset from GMT (Greenwich Mean Time) */
/* specified as minutes */
/* */
/* Output string format will depend upon values present in the */
/* "SX_DATE_TIME" structure as follows: */
/* */
/* 'useTZ = SD_TRUE' 'tz = nn..n' "yyyy-mm-ddThh:mm:ss.fffff+/-hh:mm" */
/* 'useTZ = SD_TRUE' 'tz = nn..n' "yyyy-mm-ddThh:mm:ss+/-hh:mm" */
/* */
/* 'useTZ = SD_TRUE' 'tz = 0' "yyyy-mm-ddThh:mm:ss.fffffZ" */
/* 'useTZ = SD_TRUE' 'tz = 0' "yyyy-mm-ddThh:mm:ssZ" */
/* */
/* 'useTZ = SD_FALSE' 'tz ignored' "yyyy-mm-ddThh:mm:ss.fffff" */
/* 'useTZ = SD_FALSE' 'tz ignored' "yyyy-mm-ddThh:mm:ss" */
/* */
/************************************************************************/
ST_RET UtcValueToXmlString (ST_CHAR *dest, ST_UINT destLen,
SX_DATE_TIME *sxDateTime)
{
ST_CHAR theFraction[64], theTZ[64];
ST_CHAR tzSign = '+';
ST_INT tzValue;
#define MAX_DATE_TIME_STR_LEN 32 /* longest output format: */
/* "yyyy-mm-ddThh:mm:ss.fffff+/-hh:mm" */
if (destLen < MAX_DATE_TIME_STR_LEN)
{
SLOGALWAYS2 ("ERROR: Buffer %d bytes may be too small for XML string (min=%d)",
destLen, MAX_DATE_TIME_STR_LEN);
return (SD_FAILURE);
}
/* get the date and time from the "dateTime" or
number of seconds from 1/1/1970 (UTC time)
stored in "sxDateTime" and place it in a
structure of type "tm" returned from the call
to "gmtime" as:
sec
min
hour
day of month
month
year
day of week
day of year */
/* from the data stored in the structure utilize
"strftime" to format a date and time string using
the following output format:
%Y is year with century as a decimal number
%m is month as a decimal number (01-12)
%d is day of month as a decimal number (01-31)
%H is hour in 24 hour format (00-23)
%M is minute as decimal number (00-59)
%S is second as decimal number (00-59) */
if (sxDateTime->useTZ && sxDateTime->tz == 0)
strftime (dest, destLen, "%Y-%m-%dT%H:%M:%S", gmtime (&sxDateTime->dateTime));
else
strftime (dest, destLen, "%Y-%m-%dT%H:%M:%S", localtime (&sxDateTime->dateTime));
/* then get number of microseconds stored as an */
/* "long" value in the "sxDateTime" structure and */
/* format it as a string with a leading decimal point */
if (sxDateTime->useMicroseconds)
{
/* this code was merged from time_str2.c */
/* number of microsecond digits changed from 5 to to 6 */
sprintf (theFraction, ".%06ld", sxDateTime->microseconds);
strcat (dest, theFraction);
}
/* now get the number of minutes stored as an integer */
/* for time zone offset and place it in a string */
/* in the format of "+hh:mm" or "-hh:mm" */
if (sxDateTime->useTZ)
{
tzValue = sxDateTime->tz;
/* there are 1440 minutes in a day */
/* if tzValue is outsize this value +/- it is invalid */
/* if so, we don't know the correct zone but will default to zero */
/* we are allowing +/- 23:59 just to be tolerant, but usually only */
/* only +/- 12 hours is actually used. */
if ((tzValue >= 1440) || (tzValue <= -1440))
{
SLOGALWAYS1 ("ERROR: UtcValueToXmlString sxDateTime.tz value %d out of range",
sxDateTime->tz);
strcat (dest, "Z"); /* assume GMT in case our retcode is ignored */
return SD_FAILURE;
}
if (tzValue == 0)
/* output format: "yyyy-mm-ddThh:mm:ss.fffffZ" */
strcat (dest, "Z");
else
{
/* output format: "yyyy-mm-ddThh:mm:ss.fffff+/-hh:mm" */
if (tzValue < 0)
{
tzSign = '-';
tzValue = -tzValue;
}
sprintf (theTZ, "%c%02d:%02d", tzSign, tzValue / 60, tzValue % 60);
strcat (dest, theTZ);
}
}
return SD_SUCCESS;
}
/************************************************************************/
/* CalculateTimeZoneOffset */
/************************************************************************/
/* Figures out the difference between UTC/GMT/Zulu time and */
/* local time. */
/* This difference can be used after the "mktime" */
/* function is called. The "mktime" function returns local time */
/* and to convert this time to a UTC time this calculated */
/* adjustment must be added to the local time value returned */
/* from "mktime". */
/************************************************************************/
/* NOTE: this function may no longer be necessary */
ST_DOUBLE CalculateTimeZoneOffset (ST_VOID)
{
time_t currTime, local_t, utc_t;
struct tm *pJunkTm, localTm, utcTm;
ST_DOUBLE timeZoneAdjustment;
currTime = time (NULL); /* get the current system time */
pJunkTm = gmtime (&currTime); /* convert current time value in seconds to a structure */
memcpy (&utcTm, pJunkTm, sizeof (utcTm)); /* save current UTC time */
pJunkTm = localtime (&currTime); /* convert current time value and correct for local time zone */
memcpy (&localTm, pJunkTm, sizeof (localTm)); /* save local UTC time */
utc_t = mktime (&utcTm); /* convert UTC time to UTC seconds */
local_t = mktime (&localTm); /* convert local time to UTC seconds */
timeZoneAdjustment = difftime (local_t, utc_t); /* find the difference or time zone offset */
return (timeZoneAdjustment);
}
/* this code was merged from time_str2.c */
/************************************************************************/
/* GetTimeAndUsec */
/************************************************************************/
/* a function to supply current time as a time_t, and the number of */
/* microseconds, in a single call. this will simplify the setting of */
/* data structures such as DateTime which have both of these values. */
/************************************************************************/
#if defined(_WIN32) || defined(linux) || defined(__QNX__)
time_t GetTimeAndUsec (long *usec)
{
#ifdef _WIN32
struct _timeb tb;
_ftime (&tb);
#else
struct timeb tb;
ftime (&tb);
#endif
if (usec != NULL)
{
*usec = ((long)tb.millitm) * 1000;
}
return tb.time;
}
#endif /* some platforms */
/*****************************************************************************/
/* usr_mkgmtime_isleap */
/* return 1 if 'year' is a leap year, else return 0 */
/*****************************************************************************/
static time_t usr_mkgmtime_isleap (time_t year)
{
if ((year < 0 ) || (year > 32767))
{
return 0; /* assume invalid years are not leap years */
}
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 */
} /* usr_mkgmtime_isleap */
/*****************************************************************************/
/* usr_mkgmtime_leap_year_days */
/* return number leap-year days based on 'year' */
/*****************************************************************************/
static time_t usr_mkgmtime_leap_year_days (time_t year)
{
/* number of 4000-year multiples */
time_t n4000 = (year / (time_t) 4000);
/* number of 400-year multiples in excess of 4000 */
time_t n400 = (year % (time_t) 4000) / (time_t) 400;
/* number of 100-year multiples in excess of 400 */
time_t n100 = (year % (time_t) 400) / (time_t) 100;
/* number of 4-year multiples in excess of 100 */
time_t n4 = (year % (time_t) 100) / (time_t) 4;
return
( (time_t) 969 * n4000 )
+ ( (time_t) 97 * n400 )
+ ( (time_t) 24 * n100 )
+ ( (time_t) 1 * n4 );
} /* usr_mkgmtime_leap_year_days */
/*****************************************************************************/
/* usr_mkgmtime */
/* a portable implementation of mkgmtime(), a GMT-based mktime() function. */
/* */
/* note: unlike typical system implementations of mktime(), this function */
/* does NOT normalize the struct tm fields, which are required to be valid. */
/* */
/* for consistency, tm_isdst should be set to 0 upon entry (since GMT does */
/* not use daylight savings time), but the function ignores this field. */
/* (when tm_isdst is set to 0, the system mktime() api could be used to */
/* normalize the input fields prior to calling usr_mkgmtime, if necessary.) */
/* */
/* the output fields tm_wday and tm_yday are correctly set upon exit when */
/* the result time_t value is normal. */
/* */
/* upon error, a value of (time_t) (-1) is returned, and the output fields */
/* tm_wday and tm_yday are not set in that case. detected errors are: NULL */
/* pointer to the struct tm, date/time values out of valid range, and any */
/* input that results in a negative time_t value. note that the range of */
/* input values that could produce a negative time_t are dependent on the */
/* datatype of time_t, which could be 32 or 64-bit, and signed or unsigned */
/* on some platforms. */
/*****************************************************************************/
#define USR_MKGMTIME_BASE_DAY 719527
time_t usr_mkgmtime (
struct tm * t)
{
time_t w_time_t;
time_t this_year, this_mon, this_day, leap, max_day;
int wday;
static int usr_mkgmtime_days_per_mon [13] =
{ 0,
31, /* JAN */
28, /* FEB */
31, /* MAR */
30, /* APR */
31, /* MAY */
30, /* JUN */
31, /* JUL */
31, /* AUG */
30, /* SEP */
31, /* OCT */
30, /* NOV */
31 /* DEC */
};
/* offset from start of year for a given month. */
/* to compensate for normal day numbers starting with 1, */
/* we pre-subtract 1 to optimize the calculation. */
static int usr_mkgmtime_mon_offset [13] =
{ 0,
0-1, /* JAN */ /* so Jan 1 is day 0 */
31-1, /* FEB */
59-1, /* MAR */
90-1, /* APR */
120-1, /* MAY */
151-1, /* JUN */
181-1, /* JUL */
212-1, /* AUG */
243-1, /* SEP */
273-1, /* OCT */
304-1, /* NOV */
334-1 /* DEC */
};
/* validate struct tm input values */
if (t == NULL)
{
return (time_t) (-1);
}
this_year = t->tm_year + 1900;
this_mon = t->tm_mon + 1;
this_day = t->tm_mday;
if ( (this_year < 1970)
|| (this_mon < 1)
|| (this_mon > 12)
|| (this_day < 1)
|| (t->tm_hour < 0)
|| (t->tm_hour > 23)
|| (t->tm_min < 0)
|| (t->tm_min > 59)
|| (t->tm_sec < 0)
|| (t->tm_sec > 59) )
{
return (time_t) (-1);
}
leap = usr_mkgmtime_isleap (this_year);
max_day = usr_mkgmtime_days_per_mon[this_mon];
if (this_mon == (time_t) 2)
{
max_day += leap;
}
if (this_day > max_day)
{
return (time_t) (-1);
}
/* form number of days for given year. we start by determining number */
/* of leap-year days in the years prior to current one */
w_time_t = (time_t) (((time_t) 365 * this_year) +
usr_mkgmtime_leap_year_days (this_year-1));
/* convert year/mon to days using month-offset table */
/* we must account for the fact that Jan 1 is day 0 of a year. */
/* so, "day number" is one less than the number of days */
/* by subtracting 1 from the day-offset of a given month */
/* however, since this value will be constant for a given month, */
/* the offset table already applies the '-1' factor, to save time. */
/* see the comments above for the table's definition. */
this_day += (time_t) usr_mkgmtime_mon_offset [this_mon];
if (this_mon > (time_t) 2)
{
this_day += leap; /* leap-year day's effect doesn't occur until March */
}
/* calculate base day of Jan 1 1970. 1970 was not a leap year, */
/* so the number of leap-year days of 1970 is the same as 1969, */
/* but to be consistent, we call the function using (1970-1). */
/* since 1,970 years go from the (theoretical) year 0 to 1969, */
/* the two parts of this equation are consistent. */
/* the following would calculate the base day value */
/* however, since the values are all constants, the same base day will be */
/* produced each time. running the equation with these constants produces */
/* the number 719527, which we use in order to save time. */
/* USR_MKGMTIME_BASE_DAY = (time_t) (1970 * 365) */
/* + usr_mkgmtime_leap_year_days ((time_t) (1970-1)) */
w_time_t += (time_t) (this_day - ((time_t) USR_MKGMTIME_BASE_DAY));
/* create output fields: tm_wday and tm_yday */
/* the unix epoch of 1970-01-01 was a Thursday, or tm_wday == 4, */
/* tm_wday: Day of week (0 ?6; Sunday = 0) */
/* tm_yday: Day of year (0 ?365; January 1 = 0) */
wday = ((int) w_time_t + 4) % 7;
w_time_t = (w_time_t * (time_t) 24) + (time_t) t->tm_hour;
w_time_t = (w_time_t * (time_t) 60) + (time_t) t->tm_min;
w_time_t = (w_time_t * (time_t) 60) + (time_t) t->tm_sec;
if (w_time_t < (time_t) 0)
{
return (time_t) (-1);
}
/* store output fields after time_t has been validated */
t->tm_wday = wday;
t->tm_yday = (int) this_day;;
return w_time_t;
} /* usr_mkgmtime */