/* The datetime object. It contains date and time related functions.
*
* Module begun 2008-03-05 by Rainer Gerhards, based on some code
* from syslogd.c. The main intension was to move code out of syslogd.c
* in a useful manner. It is still undecided if all functions will continue
* to stay here or some will be moved into parser modules (once we have them).
*
* Copyright 2008 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of the rsyslog runtime library.
*
* The rsyslog runtime library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The rsyslog runtime library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the rsyslog runtime library. If not, see .
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
* A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
*/
#include "config.h"
#include
#include
#include
#include
#include
#ifdef HAVE_SYS_TIME_H
# include
#endif
#include "rsyslog.h"
#include "obj.h"
#include "modules.h"
#include "datetime.h"
#include "sysvar.h"
#include "srUtils.h"
#include "stringbuf.h"
#include "errmsg.h"
/* static data */
DEFobjStaticHelpers
DEFobjCurrIf(errmsg)
/* the following table of ten powers saves us some computation */
static const int tenPowers[6] = { 1, 10, 100, 1000, 10000, 100000 };
/* ------------------------------ methods ------------------------------ */
/**
* Get the current date/time in the best resolution the operating
* system has to offer (well, actually at most down to the milli-
* second level.
*
* The date and time is returned in separate fields as this is
* most portable and removes the need for additional structures
* (but I have to admit it is somewhat "bulky";)).
*
* Obviously, *t must not be NULL...
*
* rgerhards, 2008-10-07: added ttSeconds to provide a way to
* obtain the second-resolution UNIX timestamp. This is needed
* in some situations to minimize time() calls (namely when doing
* output processing). This can be left NULL if not needed.
*/
static void getCurrTime(struct syslogTime *t, time_t *ttSeconds)
{
struct timeval tp;
struct tm *tm;
struct tm tmBuf;
long lBias;
# if defined(__hpux)
struct timezone tz;
# endif
assert(t != NULL);
# if defined(__hpux)
/* TODO: check this: under HP UX, the tz information is actually valid
* data. So we need to obtain and process it there.
*/
gettimeofday(&tp, &tz);
# else
gettimeofday(&tp, NULL);
# endif
if(ttSeconds != NULL)
*ttSeconds = tp.tv_sec;
tm = localtime_r((time_t*) &(tp.tv_sec), &tmBuf);
t->year = tm->tm_year + 1900;
t->month = tm->tm_mon + 1;
t->day = tm->tm_mday;
t->hour = tm->tm_hour;
t->minute = tm->tm_min;
t->second = tm->tm_sec;
t->secfrac = tp.tv_usec;
t->secfracPrecision = 6;
# if __sun
/* Solaris uses a different method of exporting the time zone.
* It is UTC - localtime, which is the opposite sign of mins east of GMT.
*/
lBias = -(daylight ? altzone : timezone);
# elif defined(__hpux)
lBias = tz.tz_dsttime ? - tz.tz_minuteswest : 0;
# else
lBias = tm->tm_gmtoff;
# endif
if(lBias < 0)
{
t->OffsetMode = '-';
lBias *= -1;
}
else
t->OffsetMode = '+';
t->OffsetHour = lBias / 3600;
t->OffsetMinute = lBias % 3600;
t->timeType = TIME_TYPE_RFC5424; /* we have a high precision timestamp */
}
/* A fast alternative to getCurrTime() and time() that only obtains
* a timestamp like time() does. I was told that gettimeofday(), at
* least under Linux, is much faster than time() and I could confirm
* this testing. So I created that function as a replacement.
* rgerhards, 2009-11-12
*/
static time_t
getTime(time_t *ttSeconds)
{
struct timeval tp;
if(gettimeofday(&tp, NULL) == -1)
return -1;
if(ttSeconds != NULL)
*ttSeconds = tp.tv_sec;
return tp.tv_sec;
}
/*******************************************************************
* BEGIN CODE-LIBLOGGING *
*******************************************************************
* Code in this section is borrowed from liblogging. This is an
* interim solution. Once liblogging is fully integrated, this is
* to be removed (see http://www.monitorware.com/liblogging for
* more details. 2004-11-16 rgerhards
*
* Please note that the orginal liblogging code is modified so that
* it fits into the context of the current version of syslogd.c.
*
* DO NOT PUT ANY OTHER CODE IN THIS BEGIN ... END BLOCK!!!!
*/
/**
* Parse a 32 bit integer number from a string.
*
* \param ppsz Pointer to the Pointer to the string being parsed. It
* must be positioned at the first digit. Will be updated
* so that on return it points to the first character AFTER
* the integer parsed.
* \param pLenStr pointer to string length, decremented on exit by
* characters processed
* Note that if an empty string (len < 1) is passed in,
* the method always returns zero.
* \retval The number parsed.
*/
static int srSLMGParseInt32(uchar** ppsz, int *pLenStr)
{
register int i;
i = 0;
while(*pLenStr > 0 && isdigit((int) **ppsz)) {
i = i * 10 + **ppsz - '0';
++(*ppsz);
--(*pLenStr);
}
return i;
}
/**
* Parse a TIMESTAMP-3339.
* updates the parse pointer position. The pTime parameter
* is guranteed to be updated only if a new valid timestamp
* could be obtained (restriction added 2008-09-16 by rgerhards).
* This method now also checks the maximum string length it is passed.
* If a *valid* timestamp is found, the string length is decremented
* by the number of characters processed. If it is not a valid timestamp,
* the length is kept unmodified. -- rgerhards, 2009-09-23
*/
static rsRetVal
ParseTIMESTAMP3339(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
{
uchar *pszTS = *ppszTS;
/* variables to temporarily hold time information while we parse */
int year;
int month;
int day;
int hour; /* 24 hour clock */
int minute;
int second;
int secfrac; /* fractional seconds (must be 32 bit!) */
int secfracPrecision;
char OffsetMode; /* UTC offset + or - */
char OffsetHour; /* UTC offset in hours */
int OffsetMinute; /* UTC offset in minutes */
int lenStr;
/* end variables to temporarily hold time information while we parse */
DEFiRet;
assert(pTime != NULL);
assert(ppszTS != NULL);
assert(pszTS != NULL);
lenStr = *pLenStr;
year = srSLMGParseInt32(&pszTS, &lenStr);
/* We take the liberty to accept slightly malformed timestamps e.g. in
* the format of 2003-9-1T1:0:0. This doesn't hurt on receiving. Of course,
* with the current state of affairs, we would never run into this code
* here because at postion 11, there is no "T" in such cases ;)
*/
if(lenStr == 0 || *pszTS++ != '-')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
month = srSLMGParseInt32(&pszTS, &lenStr);
if(month < 1 || month > 12)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != '-')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
day = srSLMGParseInt32(&pszTS, &lenStr);
if(day < 1 || day > 31)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != 'T')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
hour = srSLMGParseInt32(&pszTS, &lenStr);
if(hour < 0 || hour > 23)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
minute = srSLMGParseInt32(&pszTS, &lenStr);
if(minute < 0 || minute > 59)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
second = srSLMGParseInt32(&pszTS, &lenStr);
if(second < 0 || second > 60)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
/* Now let's see if we have secfrac */
if(lenStr > 0 && *pszTS == '.') {
--lenStr;
uchar *pszStart = ++pszTS;
secfrac = srSLMGParseInt32(&pszTS, &lenStr);
secfracPrecision = (int) (pszTS - pszStart);
} else {
secfracPrecision = 0;
secfrac = 0;
}
/* check the timezone */
if(lenStr == 0)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(*pszTS == 'Z') {
--lenStr;
pszTS++; /* eat Z */
OffsetMode = 'Z';
OffsetHour = 0;
OffsetMinute = 0;
} else if((*pszTS == '+') || (*pszTS == '-')) {
OffsetMode = *pszTS;
--lenStr;
pszTS++;
OffsetHour = srSLMGParseInt32(&pszTS, &lenStr);
if(OffsetHour < 0 || OffsetHour > 23)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
OffsetMinute = srSLMGParseInt32(&pszTS, &lenStr);
if(OffsetMinute < 0 || OffsetMinute > 59)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else {
/* there MUST be TZ information */
ABORT_FINALIZE(RS_RET_INVLD_TIME);
}
/* OK, we actually have a 3339 timestamp, so let's indicated this */
if(lenStr > 0) {
if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time - 2010-02-22 rgerhards */
ABORT_FINALIZE(RS_RET_INVLD_TIME);
++pszTS; /* just skip past it */
--lenStr;
}
/* we had success, so update parse pointer and caller-provided timestamp */
*ppszTS = pszTS;
pTime->timeType = 2;
pTime->year = year;
pTime->month = month;
pTime->day = day;
pTime->hour = hour;
pTime->minute = minute;
pTime->second = second;
pTime->secfrac = secfrac;
pTime->secfracPrecision = secfracPrecision;
pTime->OffsetMode = OffsetMode;
pTime->OffsetHour = OffsetHour;
pTime->OffsetMinute = OffsetMinute;
*pLenStr = lenStr;
finalize_it:
RETiRet;
}
/**
* Parse a TIMESTAMP-3164. The pTime parameter
* is guranteed to be updated only if a new valid timestamp
* could be obtained (restriction added 2008-09-16 by rgerhards). This
* also means the caller *must* provide a valid (probably current)
* timstamp in pTime when calling this function. a 3164 timestamp contains
* only partial information and only that partial information is updated.
* So the "output timestamp" is a valid timestamp only if the "input
* timestamp" was valid, too. The is actually an optimization, as it
* permits us to use a pre-aquired timestamp and thus avoids to do
* a (costly) time() call. Thanks to David Lang for insisting on
* time() call reduction ;).
* This method now also checks the maximum string length it is passed.
* If a *valid* timestamp is found, the string length is decremented
* by the number of characters processed. If it is not a valid timestamp,
* the length is kept unmodified. -- rgerhards, 2009-09-23
*/
static rsRetVal
ParseTIMESTAMP3164(struct syslogTime *pTime, uchar** ppszTS, int *pLenStr)
{
/* variables to temporarily hold time information while we parse */
int month;
int day;
int year = 0; /* 0 means no year provided */
int hour; /* 24 hour clock */
int minute;
int second;
/* end variables to temporarily hold time information while we parse */
int lenStr;
uchar *pszTS;
DEFiRet;
assert(ppszTS != NULL);
pszTS = *ppszTS;
assert(pszTS != NULL);
assert(pTime != NULL);
assert(pLenStr != NULL);
lenStr = *pLenStr;
/* If we look at the month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec),
* we may see the following character sequences occur:
*
* J(an/u(n/l)), Feb, Ma(r/y), A(pr/ug), Sep, Oct, Nov, Dec
*
* We will use this for parsing, as it probably is the
* fastest way to parse it.
*
* 2009-08-17: we now do case-insensitive comparisons, as some devices obviously do not
* obey to the RFC-specified case. As we need to guess in any case, we can ignore case
* in the first place -- rgerhards
*
* 2005-07-18, well sometimes it pays to be a bit more verbose, even in C...
* Fixed a bug that lead to invalid detection of the data. The issue was that
* we had an if(++pszTS == 'x') inside of some of the consturcts below. However,
* there were also some elseifs (doing the same ++), which than obviously did not
* check the orginal character but the next one. Now removed the ++ and put it
* into the statements below. Was a really nasty bug... I didn't detect it before
* june, when it first manifested. This also lead to invalid parsing of the rest
* of the message, as the time stamp was not detected to be correct. - rgerhards
*/
if(lenStr < 3)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
switch(*pszTS++)
{
case 'j':
case 'J':
if(*pszTS == 'a' || *pszTS == 'A') {
++pszTS;
if(*pszTS == 'n' || *pszTS == 'N') {
++pszTS;
month = 1;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else if(*pszTS == 'u' || *pszTS == 'U') {
++pszTS;
if(*pszTS == 'n' || *pszTS == 'N') {
++pszTS;
month = 6;
} else if(*pszTS == 'l' || *pszTS == 'L') {
++pszTS;
month = 7;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
case 'f':
case 'F':
if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
if(*pszTS == 'b' || *pszTS == 'B') {
++pszTS;
month = 2;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
case 'm':
case 'M':
if(*pszTS == 'a' || *pszTS == 'A') {
++pszTS;
if(*pszTS == 'r' || *pszTS == 'R') {
++pszTS;
month = 3;
} else if(*pszTS == 'y' || *pszTS == 'Y') {
++pszTS;
month = 5;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
case 'a':
case 'A':
if(*pszTS == 'p' || *pszTS == 'P') {
++pszTS;
if(*pszTS == 'r' || *pszTS == 'R') {
++pszTS;
month = 4;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else if(*pszTS == 'u' || *pszTS == 'U') {
++pszTS;
if(*pszTS == 'g' || *pszTS == 'G') {
++pszTS;
month = 8;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
case 's':
case 'S':
if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
if(*pszTS == 'p' || *pszTS == 'P') {
++pszTS;
month = 9;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
case 'o':
case 'O':
if(*pszTS == 'c' || *pszTS == 'C') {
++pszTS;
if(*pszTS == 't' || *pszTS == 'T') {
++pszTS;
month = 10;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
case 'n':
case 'N':
if(*pszTS == 'o' || *pszTS == 'O') {
++pszTS;
if(*pszTS == 'v' || *pszTS == 'V') {
++pszTS;
month = 11;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
case 'd':
case 'D':
if(*pszTS == 'e' || *pszTS == 'E') {
++pszTS;
if(*pszTS == 'c' || *pszTS == 'C') {
++pszTS;
month = 12;
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
} else
ABORT_FINALIZE(RS_RET_INVLD_TIME);
break;
default:
ABORT_FINALIZE(RS_RET_INVLD_TIME);
}
lenStr -= 3;
/* done month */
if(lenStr == 0 || *pszTS++ != ' ')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
/* we accept a slightly malformed timestamp when receiving. This is
* we accept one-digit days
*/
if(*pszTS == ' ') {
--lenStr;
++pszTS;
}
day = srSLMGParseInt32(&pszTS, &lenStr);
if(day < 1 || day > 31)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != ' ')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
/* time part */
hour = srSLMGParseInt32(&pszTS, &lenStr);
if(hour > 1970 && hour < 2100) {
/* if so, we assume this actually is a year. This is a format found
* e.g. in Cisco devices.
* (if you read this 2100+ trying to fix a bug, congratulate me
* to how long the code survived - me no longer ;)) -- rgerhards, 2008-11-18
*/
year = hour;
/* re-query the hour, this time it must be valid */
if(lenStr == 0 || *pszTS++ != ' ')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
hour = srSLMGParseInt32(&pszTS, &lenStr);
}
if(hour < 0 || hour > 23)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
minute = srSLMGParseInt32(&pszTS, &lenStr);
if(minute < 0 || minute > 59)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
if(lenStr == 0 || *pszTS++ != ':')
ABORT_FINALIZE(RS_RET_INVLD_TIME);
--lenStr;
second = srSLMGParseInt32(&pszTS, &lenStr);
if(second < 0 || second > 60)
ABORT_FINALIZE(RS_RET_INVLD_TIME);
/* we provide support for an extra ":" after the date. While this is an
* invalid format, it occurs frequently enough (e.g. with Cisco devices)
* to permit it as a valid case. -- rgerhards, 2008-09-12
*/
if(lenStr > 0 && *pszTS == ':') {
++pszTS; /* just skip past it */
--lenStr;
}
if(lenStr > 0) {
if(*pszTS != ' ') /* if it is not a space, it can not be a "good" time - 2010-02-22 rgerhards */
ABORT_FINALIZE(RS_RET_INVLD_TIME);
++pszTS; /* just skip past it */
--lenStr;
}
/* we had success, so update parse pointer and caller-provided timestamp
* fields we do not have are not updated in the caller's timestamp. This
* is the reason why the caller must pass in a correct timestamp.
*/
*ppszTS = pszTS; /* provide updated parse position back to caller */
pTime->timeType = 1;
pTime->month = month;
if(year > 0)
pTime->year = year; /* persist year if detected */
pTime->day = day;
pTime->hour = hour;
pTime->minute = minute;
pTime->second = second;
pTime->secfracPrecision = 0;
pTime->secfrac = 0;
*pLenStr = lenStr;
finalize_it:
RETiRet;
}
/*******************************************************************
* END CODE-LIBLOGGING *
*******************************************************************/
/**
* Format a syslogTimestamp into format required by MySQL.
* We are using the 14 digits format. For example 20041111122600
* is interpreted as '2004-11-11 12:26:00'.
* The caller must provide the timestamp as well as a character
* buffer that will receive the resulting string. The function
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
*/
int formatTimestampToMySQL(struct syslogTime *ts, char* pBuf)
{
/* currently we do not consider localtime/utc. This may later be
* added. If so, I recommend using a property replacer option
* and/or a global configuration option. However, we should wait
* on user requests for this feature before doing anything.
* rgerhards, 2007-06-26
*/
assert(ts != NULL);
assert(pBuf != NULL);
pBuf[0] = (ts->year / 1000) % 10 + '0';
pBuf[1] = (ts->year / 100) % 10 + '0';
pBuf[2] = (ts->year / 10) % 10 + '0';
pBuf[3] = ts->year % 10 + '0';
pBuf[4] = (ts->month / 10) % 10 + '0';
pBuf[5] = ts->month % 10 + '0';
pBuf[6] = (ts->day / 10) % 10 + '0';
pBuf[7] = ts->day % 10 + '0';
pBuf[8] = (ts->hour / 10) % 10 + '0';
pBuf[9] = ts->hour % 10 + '0';
pBuf[10] = (ts->minute / 10) % 10 + '0';
pBuf[11] = ts->minute % 10 + '0';
pBuf[12] = (ts->second / 10) % 10 + '0';
pBuf[13] = ts->second % 10 + '0';
pBuf[14] = '\0';
return 15;
}
int formatTimestampToPgSQL(struct syslogTime *ts, char *pBuf)
{
/* see note in formatTimestampToMySQL, applies here as well */
assert(ts != NULL);
assert(pBuf != NULL);
pBuf[0] = (ts->year / 1000) % 10 + '0';
pBuf[1] = (ts->year / 100) % 10 + '0';
pBuf[2] = (ts->year / 10) % 10 + '0';
pBuf[3] = ts->year % 10 + '0';
pBuf[4] = '-';
pBuf[5] = (ts->month / 10) % 10 + '0';
pBuf[6] = ts->month % 10 + '0';
pBuf[7] = '-';
pBuf[8] = (ts->day / 10) % 10 + '0';
pBuf[9] = ts->day % 10 + '0';
pBuf[10] = ' ';
pBuf[11] = (ts->hour / 10) % 10 + '0';
pBuf[12] = ts->hour % 10 + '0';
pBuf[13] = ':';
pBuf[14] = (ts->minute / 10) % 10 + '0';
pBuf[15] = ts->minute % 10 + '0';
pBuf[16] = ':';
pBuf[17] = (ts->second / 10) % 10 + '0';
pBuf[18] = ts->second % 10 + '0';
pBuf[19] = '\0';
return 19;
}
/**
* Format a syslogTimestamp to just the fractional seconds.
* The caller must provide the timestamp as well as a character
* buffer that will receive the resulting string. The function
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
* The buffer must be at least 7 bytes large.
* rgerhards, 2008-06-06
*/
int formatTimestampSecFrac(struct syslogTime *ts, char* pBuf)
{
int iBuf;
int power;
int secfrac;
short digit;
assert(ts != NULL);
assert(pBuf != NULL);
iBuf = 0;
if(ts->secfracPrecision > 0)
{
power = tenPowers[(ts->secfracPrecision - 1) % 6];
secfrac = ts->secfrac;
while(power > 0) {
digit = secfrac / power;
secfrac -= digit * power;
power /= 10;
pBuf[iBuf++] = digit + '0';
}
} else {
pBuf[iBuf++] = '0';
}
pBuf[iBuf] = '\0';
return iBuf;
}
/**
* Format a syslogTimestamp to a RFC3339 timestamp string (as
* specified in syslog-protocol).
* The caller must provide the timestamp as well as a character
* buffer that will receive the resulting string. The function
* returns the size of the timestamp written in bytes (without
* the string terminator). If 0 is returend, an error occured.
*/
int formatTimestamp3339(struct syslogTime *ts, char* pBuf)
{
int iBuf;
int power;
int secfrac;
short digit;
BEGINfunc
assert(ts != NULL);
assert(pBuf != NULL);
/* start with fixed parts */
/* year yyyy */
pBuf[0] = (ts->year / 1000) % 10 + '0';
pBuf[1] = (ts->year / 100) % 10 + '0';
pBuf[2] = (ts->year / 10) % 10 + '0';
pBuf[3] = ts->year % 10 + '0';
pBuf[4] = '-';
/* month */
pBuf[5] = (ts->month / 10) % 10 + '0';
pBuf[6] = ts->month % 10 + '0';
pBuf[7] = '-';
/* day */
pBuf[8] = (ts->day / 10) % 10 + '0';
pBuf[9] = ts->day % 10 + '0';
pBuf[10] = 'T';
/* hour */
pBuf[11] = (ts->hour / 10) % 10 + '0';
pBuf[12] = ts->hour % 10 + '0';
pBuf[13] = ':';
/* minute */
pBuf[14] = (ts->minute / 10) % 10 + '0';
pBuf[15] = ts->minute % 10 + '0';
pBuf[16] = ':';
/* second */
pBuf[17] = (ts->second / 10) % 10 + '0';
pBuf[18] = ts->second % 10 + '0';
iBuf = 19; /* points to next free entry, now it becomes dynamic! */
if(ts->secfracPrecision > 0) {
pBuf[iBuf++] = '.';
power = tenPowers[(ts->secfracPrecision - 1) % 6];
secfrac = ts->secfrac;
while(power > 0) {
digit = secfrac / power;
secfrac -= digit * power;
power /= 10;
pBuf[iBuf++] = digit + '0';
}
}
if(ts->OffsetMode == 'Z') {
pBuf[iBuf++] = 'Z';
} else {
pBuf[iBuf++] = ts->OffsetMode;
pBuf[iBuf++] = (ts->OffsetHour / 10) % 10 + '0';
pBuf[iBuf++] = ts->OffsetHour % 10 + '0';
pBuf[iBuf++] = ':';
pBuf[iBuf++] = (ts->OffsetMinute / 10) % 10 + '0';
pBuf[iBuf++] = ts->OffsetMinute % 10 + '0';
}
pBuf[iBuf] = '\0';
ENDfunc
return iBuf;
}
/**
* Format a syslogTimestamp to a RFC3164 timestamp sring.
* The caller must provide the timestamp as well as a character
* buffer that will receive the resulting string. The function
* returns the size of the timestamp written in bytes (without
* the string termnator). If 0 is returend, an error occured.
*/
int formatTimestamp3164(struct syslogTime *ts, char* pBuf)
{
static char* monthNames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
int iDay;
assert(ts != NULL);
assert(pBuf != NULL);
pBuf[0] = monthNames[(ts->month - 1)% 12][0];
pBuf[1] = monthNames[(ts->month - 1) % 12][1];
pBuf[2] = monthNames[(ts->month - 1) % 12][2];
pBuf[3] = ' ';
iDay = (ts->day / 10) % 10; /* we need to write a space if the first digit is 0 */
pBuf[4] = iDay ? iDay + '0' : ' ';
pBuf[5] = ts->day % 10 + '0';
pBuf[6] = ' ';
pBuf[7] = (ts->hour / 10) % 10 + '0';
pBuf[8] = ts->hour % 10 + '0';
pBuf[9] = ':';
pBuf[10] = (ts->minute / 10) % 10 + '0';
pBuf[11] = ts->minute % 10 + '0';
pBuf[12] = ':';
pBuf[13] = (ts->second / 10) % 10 + '0';
pBuf[14] = ts->second % 10 + '0';
pBuf[15] = '\0';
return 16; /* traditional: number of bytes written */
}
/* queryInterface function
* rgerhards, 2008-03-05
*/
BEGINobjQueryInterface(datetime)
CODESTARTobjQueryInterface(datetime)
if(pIf->ifVersion != datetimeCURR_IF_VERSION) { /* check for current version, increment on each change */
ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
}
/* ok, we have the right interface, so let's fill it
* Please note that we may also do some backwards-compatibility
* work here (if we can support an older interface version - that,
* of course, also affects the "if" above).
*/
pIf->getCurrTime = getCurrTime;
pIf->GetTime = getTime;
pIf->ParseTIMESTAMP3339 = ParseTIMESTAMP3339;
pIf->ParseTIMESTAMP3164 = ParseTIMESTAMP3164;
pIf->formatTimestampToMySQL = formatTimestampToMySQL;
pIf->formatTimestampToPgSQL = formatTimestampToPgSQL;
pIf->formatTimestampSecFrac = formatTimestampSecFrac;
pIf->formatTimestamp3339 = formatTimestamp3339;
pIf->formatTimestamp3164 = formatTimestamp3164;
finalize_it:
ENDobjQueryInterface(datetime)
/* Initialize the datetime class. Must be called as the very first method
* before anything else is called inside this class.
* rgerhards, 2008-02-19
*/
BEGINAbstractObjClassInit(datetime, 1, OBJ_IS_CORE_MODULE) /* class, version */
/* request objects we use */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
ENDObjClassInit(datetime)
/* vi:set ai:
*/