481 lines
15 KiB
C++
481 lines
15 KiB
C++
/*************************************************************************\
|
|
* Copyright (c) 2011 LANS LLC, as Operator of
|
|
* Los Alamos National Laboratory.
|
|
* Copyright (c) 2002 The Regents of the University of California, as
|
|
* Operator of Los Alamos National Laboratory.
|
|
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
|
|
* National Laboratory.
|
|
* SPDX-License-Identifier: EPICS
|
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
/* epicsTime.cpp */
|
|
/* Author Jeffrey O. Hill */
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <ctype.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
#include <string> // vxWorks 6.0 requires this include
|
|
|
|
#include "errSymTbl.h"
|
|
#include "epicsAssert.h"
|
|
#include "epicsVersion.h"
|
|
#include "epicsStdlib.h"
|
|
#include "epicsMath.h"
|
|
#include "envDefs.h"
|
|
#include "epicsTime.h"
|
|
#include "osiSock.h" /* pull in struct timeval */
|
|
#include "epicsStdio.h"
|
|
|
|
static const epicsUInt32 nSecPerSec = 1000000000u;
|
|
static const unsigned nSecFracDigits = 9u;
|
|
|
|
|
|
void epicsTime::throwError(int code)
|
|
{
|
|
if(code==epicsTimeOK)
|
|
return;
|
|
throw std::logic_error(errSymMsg(code));
|
|
}
|
|
|
|
|
|
epicsTime::epicsTime ( const epicsTimeStamp & replace )
|
|
:ts(replace)
|
|
{
|
|
if(ts.nsec >= nSecPerSec)
|
|
throw std::logic_error("epicsTimeStamp has overflow in nano-seconds field");
|
|
}
|
|
|
|
epicsTime epicsTime::getCurrent ()
|
|
{
|
|
epicsTimeStamp current;
|
|
int status = epicsTimeGetCurrent (¤t);
|
|
if (status) {
|
|
throw unableToFetchCurrentTime ("Unable to fetch Current Time");
|
|
}
|
|
return epicsTime ( current );
|
|
}
|
|
|
|
epicsTime epicsTime::getEvent (const epicsTimeEvent &event)
|
|
{
|
|
epicsTimeStamp current;
|
|
int status = epicsTimeGetEvent (¤t, event);
|
|
if (status) {
|
|
throw unableToFetchCurrentTime ("Unable to fetch Event Time");
|
|
}
|
|
return epicsTime ( current );
|
|
}
|
|
|
|
epicsTime::operator struct timeval () const {
|
|
timeval ret;
|
|
epicsTimeToTimeval(&ret, &ts);
|
|
return ret;
|
|
}
|
|
|
|
epicsTime::epicsTime ( const struct timeval & replace) {
|
|
throwError(epicsTimeFromTimeval(&ts, &replace));
|
|
}
|
|
|
|
epicsTime & epicsTime::operator = ( const struct timeval & replace) {
|
|
throwError(epicsTimeFromTimeval(&ts, &replace));
|
|
return *this;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& strm, const epicsTime& ts)
|
|
{
|
|
char temp[64];
|
|
|
|
(void)ts.strftime(temp, sizeof(temp), "%Y-%m-%d %H:%M:%S.%09f");
|
|
temp[sizeof(temp)-1u] = '\0';
|
|
return strm<<temp;
|
|
}
|
|
|
|
// Break up a format string into "<strftime prefix>%0<nnn>f<postfix>"
|
|
// (where <nnn> in an unsigned integer)
|
|
// Result:
|
|
// A) Copies a prefix which is valid for ANSI strftime into the supplied
|
|
// buffer setting the buffer to an empty string if no prefix is present.
|
|
// B) Indicates whether a valid "%0<n>f]" is present or not and if so
|
|
// specifying its nnnn
|
|
// C) returning a pointer to the postfix (which might be passed again
|
|
// to fracFormatFind.
|
|
static const char * fracFormatFind (
|
|
const char * const pFormat,
|
|
char * const pPrefixBuf,
|
|
const size_t prefixBufLen,
|
|
bool & fracFmtFound,
|
|
unsigned long & fracFmtWidth )
|
|
{
|
|
assert ( prefixBufLen > 1 );
|
|
unsigned long width = 0xffffffff;
|
|
bool fracFound = false;
|
|
const char * pAfter = pFormat;
|
|
const char * pFmt = pFormat;
|
|
while ( *pFmt != '\0' ) {
|
|
if ( *pFmt == '%' ) {
|
|
if ( pFmt[1] == '%' ) {
|
|
pFmt++;
|
|
}
|
|
else if ( pFmt[1] == 'f' ) {
|
|
fracFound = true;
|
|
pAfter = & pFmt[2];
|
|
break;
|
|
}
|
|
else {
|
|
errno = 0;
|
|
char * pAfterTmp;
|
|
unsigned long result = strtoul ( pFmt + 1, & pAfterTmp, 10 );
|
|
if ( errno == 0 && *pAfterTmp == 'f' && result > 0 ) {
|
|
width = result;
|
|
fracFound = true;
|
|
pAfter = pAfterTmp + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pFmt++;
|
|
pAfter = pFmt;
|
|
}
|
|
size_t len = pFmt - pFormat;
|
|
if ( len < prefixBufLen ) {
|
|
strncpy ( pPrefixBuf, pFormat, len );
|
|
pPrefixBuf [ len ] = '\0';
|
|
if ( fracFound ) {
|
|
fracFmtFound = true;
|
|
fracFmtWidth = width;
|
|
}
|
|
else {
|
|
fracFmtFound = false;
|
|
}
|
|
}
|
|
else {
|
|
strncpy ( pPrefixBuf, "<invalid format>", prefixBufLen - 1 );
|
|
pPrefixBuf [ prefixBufLen - 1 ] = '\0';
|
|
fracFmtFound = false;
|
|
pAfter = "";
|
|
}
|
|
|
|
return pAfter;
|
|
}
|
|
|
|
//
|
|
// size_t epicsTime::strftime ()
|
|
//
|
|
size_t epicsStdCall epicsTimeToStrftime (char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS)
|
|
{
|
|
if ( bufLength == 0u ) {
|
|
return 0u;
|
|
}
|
|
|
|
// presume that EPOCH date is an uninitialized time stamp
|
|
if ( pTS->secPastEpoch == 0 && pTS->nsec == 0u ) {
|
|
strncpy ( pBuff, "<undefined>", bufLength );
|
|
pBuff[bufLength-1] = '\0';
|
|
return strlen ( pBuff );
|
|
}
|
|
|
|
char * pBufCur = pBuff;
|
|
const char * pFmt = pFormat;
|
|
size_t bufLenLeft = bufLength;
|
|
while ( *pFmt != '\0' && bufLenLeft > 1 ) {
|
|
// look for "%0<n>f" at the end (used for fractional second formatting)
|
|
char strftimePrefixBuf [256];
|
|
bool fracFmtFound;
|
|
unsigned long fracWid = 0;
|
|
pFmt = fracFormatFind (
|
|
pFmt,
|
|
strftimePrefixBuf, sizeof ( strftimePrefixBuf ),
|
|
fracFmtFound, fracWid );
|
|
|
|
// nothing more in the string, then quit
|
|
if ( ! ( strftimePrefixBuf[0] != '\0' || fracFmtFound ) ) {
|
|
break;
|
|
}
|
|
// all but fractional seconds use strftime formatting
|
|
if ( strftimePrefixBuf[0] != '\0' ) {
|
|
tm tm;
|
|
(void)epicsTimeToTM(&tm, 0, pTS);
|
|
size_t strftimeNumChar = :: strftime (
|
|
pBufCur, bufLenLeft, strftimePrefixBuf, &tm );
|
|
pBufCur [ strftimeNumChar ] = '\0';
|
|
pBufCur += strftimeNumChar;
|
|
bufLenLeft -= strftimeNumChar;
|
|
}
|
|
|
|
// fractional seconds formatting
|
|
if ( fracFmtFound && bufLenLeft > 1 ) {
|
|
if ( fracWid > nSecFracDigits ) {
|
|
fracWid = nSecFracDigits;
|
|
}
|
|
// verify that there are enough chars left for the fractional seconds
|
|
if ( fracWid < bufLenLeft )
|
|
{
|
|
tm tm;
|
|
(void)epicsTimeToTM(&tm, 0, pTS);
|
|
if ( pTS->nsec < nSecPerSec ) {
|
|
// divisors for fraction (see below)
|
|
static const unsigned long div[] = {
|
|
static_cast < unsigned long > ( 1e9 ),
|
|
static_cast < unsigned long > ( 1e8 ),
|
|
static_cast < unsigned long > ( 1e7 ),
|
|
static_cast < unsigned long > ( 1e6 ),
|
|
static_cast < unsigned long > ( 1e5 ),
|
|
static_cast < unsigned long > ( 1e4 ),
|
|
static_cast < unsigned long > ( 1e3 ),
|
|
static_cast < unsigned long > ( 1e2 ),
|
|
static_cast < unsigned long > ( 1e1 ),
|
|
static_cast < unsigned long > ( 1e0 )
|
|
};
|
|
// round without overflowing into whole seconds
|
|
unsigned long frac = pTS->nsec + div[fracWid] / 2;
|
|
if (frac >= nSecPerSec)
|
|
frac = nSecPerSec - 1;
|
|
// convert nanosecs to integer of correct range
|
|
frac /= div[fracWid];
|
|
char fracFormat[32];
|
|
epicsSnprintf ( fracFormat, sizeof ( fracFormat ), "%%0%lulu", fracWid );
|
|
int status = epicsSnprintf ( pBufCur, bufLenLeft, fracFormat, frac );
|
|
if ( status > 0 ) {
|
|
unsigned long nChar = static_cast < unsigned long > ( status );
|
|
if ( nChar >= bufLenLeft ) {
|
|
nChar = bufLenLeft - 1;
|
|
}
|
|
pBufCur[nChar] = '\0';
|
|
pBufCur += nChar;
|
|
bufLenLeft -= nChar;
|
|
}
|
|
}
|
|
else {
|
|
static const char pOVF [] = "OVF";
|
|
size_t tmpLen = sizeof ( pOVF ) - 1;
|
|
if ( tmpLen >= bufLenLeft ) {
|
|
tmpLen = bufLenLeft - 1;
|
|
}
|
|
strncpy ( pBufCur, pOVF, tmpLen );
|
|
pBufCur[tmpLen] = '\0';
|
|
pBufCur += tmpLen;
|
|
bufLenLeft -= tmpLen;
|
|
}
|
|
}
|
|
else {
|
|
static const char pDoesntFit [] = "************";
|
|
size_t tmpLen = sizeof ( pDoesntFit ) - 1;
|
|
if ( tmpLen >= bufLenLeft ) {
|
|
tmpLen = bufLenLeft - 1;
|
|
}
|
|
strncpy ( pBufCur, pDoesntFit, tmpLen );
|
|
pBufCur[tmpLen] = '\0';
|
|
pBufCur += tmpLen;
|
|
bufLenLeft -= tmpLen;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pBufCur - pBuff;
|
|
}
|
|
|
|
//
|
|
// epicsTime::show (unsigned)
|
|
//
|
|
void epicsStdCall epicsTimeShow (const epicsTimeStamp *pTS, unsigned level)
|
|
{
|
|
char bigBuffer[256];
|
|
|
|
size_t numChar = epicsTimeToStrftime( bigBuffer, sizeof ( bigBuffer ),
|
|
"%a %b %d %Y %H:%M:%S.%09f", pTS );
|
|
if ( numChar > 0 ) {
|
|
printf ( "epicsTime: %s\n", bigBuffer );
|
|
}
|
|
}
|
|
|
|
int epicsStdCall epicsTimeToTime_t (time_t *pDest, const epicsTimeStamp *pSrc)
|
|
{
|
|
STATIC_ASSERT(sizeof(*pDest) >= sizeof(pSrc->secPastEpoch));
|
|
|
|
// widen to 64-bit to (eventually) accommodate 64-bit time_t
|
|
*pDest = epicsUInt64(pSrc->secPastEpoch) + POSIX_TIME_AT_EPICS_EPOCH;
|
|
return epicsTimeOK;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeFromTime_t (epicsTimeStamp *pDest, time_t src)
|
|
{
|
|
pDest->secPastEpoch = epicsInt64(src) - POSIX_TIME_AT_EPICS_EPOCH;
|
|
pDest->nsec = 0;
|
|
return epicsTimeOK;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeToTM (struct tm *pDest, unsigned long *pNSecDest, const epicsTimeStamp *pSrc)
|
|
{
|
|
time_t temp;
|
|
int err;
|
|
err = epicsTimeToTime_t(&temp, pSrc);
|
|
if(!err)
|
|
err = epicsTime_localtime(&temp, pDest);
|
|
if(!err && pNSecDest)
|
|
*pNSecDest = pSrc->nsec;
|
|
return err;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeToGMTM (struct tm *pDest, unsigned long *pNSecDest, const epicsTimeStamp *pSrc)
|
|
{
|
|
time_t temp;
|
|
int err;
|
|
err = epicsTimeToTime_t(&temp, pSrc);
|
|
if(!err)
|
|
err = epicsTime_gmtime(&temp, pDest);
|
|
if(!err && pNSecDest)
|
|
*pNSecDest = pSrc->nsec;
|
|
return err;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeFromTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc)
|
|
{
|
|
tm temp = *pSrc; // mktime() modifies (at least) tm_wday and tm_yday
|
|
time_t tsrc = mktime(&temp);
|
|
int err = epicsTimeFromTime_t(pDest, tsrc);
|
|
if(!err)
|
|
pDest->nsec = nSecSrc;
|
|
return err;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
# define timegm _mkgmtime
|
|
|
|
#elif defined(__rtems__) || defined(vxWorks)
|
|
|
|
static
|
|
time_t timegm(tm* gtm)
|
|
{
|
|
// ugly hack for targets without timegm(tm*), but which have mktime(tm*).
|
|
// probably has issues near start/end of DST
|
|
|
|
// translate to seconds as if a local time. off by TZ offset
|
|
time_t fakelocal = mktime(gtm);
|
|
// now use gmtime() which applies the TZ offset again, but with the wrong sign
|
|
tm wrongtm;
|
|
epicsTime_gmtime(&fakelocal, &wrongtm);
|
|
// translate this to seconds
|
|
time_t fakex2 = mktime(&wrongtm);
|
|
|
|
// tzoffset = fakelocal - fakex2;
|
|
|
|
return epicsInt64(fakelocal)*2 - fakex2;
|
|
}
|
|
|
|
#endif
|
|
|
|
int epicsStdCall epicsTimeFromGMTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc)
|
|
{
|
|
tm temp = *pSrc; // timegm() may modify (at least) tm_wday and tm_yday
|
|
time_t tsrc = timegm(&temp);
|
|
int err = epicsTimeFromTime_t(pDest, tsrc);
|
|
if(!err)
|
|
pDest->nsec = nSecSrc;
|
|
return err;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeToTimespec (struct timespec *pDest, const epicsTimeStamp *pSrc)
|
|
{
|
|
int err = epicsTimeToTime_t(&pDest->tv_sec, pSrc);
|
|
if(!err)
|
|
pDest->tv_nsec = pSrc->nsec;
|
|
return err;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeFromTimespec (epicsTimeStamp *pDest, const struct timespec *pSrc)
|
|
{
|
|
int err = epicsTimeFromTime_t(pDest, pSrc->tv_sec);
|
|
if(!err)
|
|
pDest->nsec = pSrc->tv_nsec;
|
|
return err;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeToTimeval (struct timeval *pDest, const epicsTimeStamp *pSrc)
|
|
{
|
|
time_t temp;
|
|
int err = epicsTimeToTime_t(&temp, pSrc);
|
|
if(!err) {
|
|
pDest->tv_sec = temp; // tv_sec is not time_t on windows
|
|
pDest->tv_usec = pSrc->nsec/1000u;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeFromTimeval (epicsTimeStamp *pDest, const struct timeval *pSrc)
|
|
{
|
|
int err = epicsTimeFromTime_t(pDest, pSrc->tv_sec);
|
|
if(!err)
|
|
pDest->nsec = pSrc->tv_usec*1000u;
|
|
return err;
|
|
}
|
|
|
|
double epicsStdCall epicsTimeDiffInSeconds (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
/* double(*pLeft - *pRight)
|
|
*
|
|
* 0xffffffff*1000000000 < 2**62
|
|
* 0x200000000*1000000002 < 2**63
|
|
* so there is (just barely) space to add 2 TSs as signed 64-bit integers without overflow
|
|
*/
|
|
|
|
// handle over/underflow as u32 when subtracting
|
|
epicsInt64 nsec = epicsInt32(pLeft->secPastEpoch - pRight->secPastEpoch);
|
|
nsec *= nSecPerSec;
|
|
nsec += epicsInt32(pLeft->nsec) - epicsInt32(pRight->nsec);
|
|
|
|
return double(nsec)*1e-9;
|
|
}
|
|
|
|
void epicsStdCall epicsTimeAddSeconds (epicsTimeStamp *pDest, double seconds)
|
|
{
|
|
epicsInt64 nsec = pDest->secPastEpoch;
|
|
nsec *= nSecPerSec;
|
|
nsec += epicsInt64(pDest->nsec);
|
|
nsec += epicsInt64(seconds*1e9 + (seconds>=0.0 ? 0.5 : -0.5));
|
|
pDest->secPastEpoch = nsec/nSecPerSec;
|
|
pDest->nsec = (nsec>=0 ? nsec : -nsec)%nSecPerSec;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
return pLeft->secPastEpoch == pRight->secPastEpoch && pLeft->nsec == pRight->nsec;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeNotEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
return !epicsTimeEqual(pLeft, pRight);
|
|
}
|
|
|
|
epicsInt64 epicsStdCall epicsTimeDiffInNS (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
epicsInt64 delta = epicsInt64(pLeft->secPastEpoch) - pRight->secPastEpoch;
|
|
delta *= nSecPerSec;
|
|
delta += epicsInt64(pLeft->nsec) - pRight->nsec;
|
|
return delta;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeLessThan (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
return epicsTimeDiffInNS(pLeft, pRight) < 0;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeLessThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
return epicsTimeDiffInNS(pLeft, pRight) <= 0;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeGreaterThan (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
return epicsTimeDiffInNS(pLeft, pRight) > 0;
|
|
}
|
|
|
|
int epicsStdCall epicsTimeGreaterThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
|
|
{
|
|
return epicsTimeDiffInNS(pLeft, pRight) >= 0;
|
|
}
|