2749 lines
80 KiB
C
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 */
|