/*************************************************************************\ * 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 #include #include #include #include #include #include #include // 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<%0f" // (where 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 "%0f]" 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, "", 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, "", 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 "%0f" 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; }