From 748f8f9131719df2c4edf91963c32423bf86b136 Mon Sep 17 00:00:00 2001 From: Marty Kraimer Date: Mon, 29 Jan 2001 20:17:09 +0000 Subject: [PATCH] replacement for osiTime --- src/libCom/osi/epicsTime.cpp | 844 +++++++++++++++++++++++++++++++++++ src/libCom/osi/epicsTime.h | 361 +++++++++++++++ 2 files changed, 1205 insertions(+) create mode 100644 src/libCom/osi/epicsTime.cpp create mode 100644 src/libCom/osi/epicsTime.h diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp new file mode 100644 index 000000000..4a9841d46 --- /dev/null +++ b/src/libCom/osi/epicsTime.cpp @@ -0,0 +1,844 @@ +/* epicsTime.cpp */ +/* Author Jeffrey O. Hill */ + +/********************COPYRIGHT NOTIFICATION********************************** +This software was developed under a United States Government license +described on the COPYRIGHT_Combined file included as part +of this distribution. +****************************************************************************/ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "locationException.h" +#include "epicsAssert.h" +#include "envDefs.h" +#include "epicsTime.h" + +// +// useful public constants +// +const unsigned epicsTime::mSecPerSec = 1000u; +const unsigned epicsTime::uSecPerSec = 1000u*epicsTime::mSecPerSec; +const unsigned epicsTime::nSecPerSec = 1000u*epicsTime::uSecPerSec; +const unsigned epicsTime::nSecPerUSec = 1000u; +const unsigned epicsTime::secPerMin = 60u; + +static const unsigned ntpEpochYear = 1900; +static const unsigned ntpEpocMonth = 0; // January +static const unsigned ntpEpocDayOfTheMonth = 1; // the 1st day of the month +static const double ULONG_MAX_PLUS_ONE = (static_cast(ULONG_MAX) + 1.0); + +// +// force this module to include code that can convert +// to GDD's aitTimeStamp, but dont require that it must +// link with gdd. Therefore, gdd.h is not included here. +// +class aitTimeStamp { +public: + unsigned long tv_sec; + unsigned long tv_nsec; +}; + +static const unsigned tmStructEpochYear = 1900; + +static const unsigned epicsEpochYear = 1990; +static const unsigned epicsEpocMonth = 0; // January +static const unsigned epicsEpocDayOfTheMonth = 1; // the 1st day of the month + +// +// forward declarations for utility routines +// +static char *fracFormat (const char *pFormat, unsigned long *width); + +// +// epicsTime (const unsigned long secIn, const unsigned long nSecIn) +// +inline epicsTime::epicsTime (const unsigned long secIn, const unsigned long nSecIn) : + secPastEpoch ( nSecIn / nSecPerSec + secIn ), nSec ( nSecIn % nSecPerSec ) {} + +// +// epicsTimeLoadTimeInit +// +class epicsTimeLoadTimeInit { +public: + epicsTimeLoadTimeInit (); + + double epicsEpochOffset; // seconds +#ifdef NTP_SUPPORT + double ntpEpochOffset; // seconds +#endif + double time_tSecPerTick; // seconds (both NTP and EPICS use int sec) +}; + +static const epicsTimeLoadTimeInit lti; + +// +// epicsTimeLoadTimeInit () +// +epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () +{ + static const time_t ansiEpoch = 0; + double secWest; + + { + time_t current = time (NULL); + time_t error; + tm date; + + gmtime_r (¤t, &date); + error = mktime (&date); + assert (error!=(time_t)-1); + secWest = difftime (error, current); + } + + { + time_t first = static_cast (0); + time_t last = static_cast (1); + this->time_tSecPerTick = difftime (last, first); + } + + { + struct tm tmEpicsEpoch; + time_t epicsEpoch; + + tmEpicsEpoch.tm_sec = 0; + tmEpicsEpoch.tm_min = 0; + tmEpicsEpoch.tm_hour = 0; + tmEpicsEpoch.tm_mday = epicsEpocDayOfTheMonth; + tmEpicsEpoch.tm_mon = epicsEpocMonth; + tmEpicsEpoch.tm_year = epicsEpochYear-tmStructEpochYear; + tmEpicsEpoch.tm_isdst = -1; // dont know if its daylight savings time + + epicsEpoch = mktime (&tmEpicsEpoch); + assert (epicsEpoch!=(time_t)-1); + this->epicsEpochOffset = difftime (epicsEpoch, ansiEpoch) - secWest; + } + +#ifdef NTP_SUPPORT + /* unfortunately, on NT mktime cant calculate a time_t for a date before 1970 */ + { + struct tm tmEpochNTP; + time_t ntpEpoch; + + tmEpochNTP.tm_sec = 0; + tmEpochNTP.tm_min = 0; + tmEpochNTP.tm_hour = 0; + tmEpochNTP.tm_mday = ntpEpocDayOfTheMonth; + tmEpochNTP.tm_mon = ntpEpocMonth; + tmEpochNTP.tm_year = ntpEpochYear-tmStructEpochYear; + tmEpochNTP.tm_isdst = -1; // dont know if its daylight savings time + + ntpEpoch = mktime (&tmEpochNTP); + assert (ntpEpoch!=(time_t)-1); + + this->ntpEpochOffset = static_cast (difftime (ansiEpoch, ntpEpoch) + this->epicsEpochOffset - secWest); + } +#endif +} + +// +// epicsTime::addNanoSec () +// +// many of the UNIX timestamp formats have nano sec stored as a long +// +inline void epicsTime::addNanoSec (long nSecAdj) +{ + double secAdj = static_cast (nSecAdj) / nSecPerSec; + *this += secAdj; +} + +// +// epicsTime (const time_t_wrapper &tv) +// +epicsTime::epicsTime (const time_t_wrapper &ansiTimeTicks) +{ + static double uLongMax = static_cast (ULONG_MAX); + double sec; + + // + // map time_t, which ansi C defines as some arithmetic type, into type double + // + sec = ansiTimeTicks.ts * lti.time_tSecPerTick - lti.epicsEpochOffset; + + // + // map into the the EPICS time stamp range (which allows rollover) + // + if (sec < 0.0) { + if (sec < -uLongMax) { + sec = sec + static_cast(-sec/uLongMax)*uLongMax; + } + sec += uLongMax; + } + else if (sec > uLongMax) { + sec = sec - static_cast(sec/uLongMax)*uLongMax; + } + + this->secPastEpoch = static_cast (sec); + this->nSec = static_cast ( (sec-this->secPastEpoch) * nSecPerSec ); +} + +// +// operator time_t_wrapper () +// +epicsTime::operator time_t_wrapper () const +{ + double tmp; + time_t_wrapper wrap; + + tmp = (this->secPastEpoch + lti.epicsEpochOffset) / lti.time_tSecPerTick; + tmp += (this->nSec / lti.time_tSecPerTick) / nSecPerSec; + + // + // map type double into time_t which ansi C defines as some arithmetic type + // + wrap.ts = static_cast (tmp); + + return wrap; +} + +// +// convert to and from ANSI C struct tm (with nano seconds) +// +epicsTime::operator tm_nano_sec () const +{ + tm_nano_sec tm; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + + // + // reentrant version of localtime() - from POSIX RT + // + // WRS returns int and others return &tm.ansi_tm on + // succes? + // + localtime_r (&ansiTimeTicks.ts, &tm.ansi_tm); + + tm.nSec = this->nSec; + + return tm; +} + +// +// epicsTime (const tm_nano_sec &tm) +// +epicsTime::epicsTime (const tm_nano_sec &tm) +{ + static const time_t mktimeFailure = static_cast (-1); + time_t_wrapper ansiTimeTicks; + struct tm tmp = tm.ansi_tm; + + ansiTimeTicks.ts = mktime (&tmp); + if (ansiTimeTicks.ts == mktimeFailure) { + throwWithLocation ( formatProblemWithStructTM () ); + } + + *this = epicsTime (ansiTimeTicks); + + unsigned long nSecAdj = tm.nSec % nSecPerSec; + unsigned long secAdj = tm.nSec / nSecPerSec; + *this = epicsTime (this->secPastEpoch+secAdj, this->nSec+nSecAdj); +} + +// +// operator struct timespec () +// +epicsTime::operator struct timespec () const +{ + struct timespec ts; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + ts.tv_sec = ansiTimeTicks.ts; + ts.tv_nsec = static_cast (this->nSec); + return ts; +} + +// +// epicsTime (const struct timespec &ts) +// +epicsTime::epicsTime (const struct timespec &ts) +{ + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks.ts = ts.tv_sec; + *this = epicsTime (ansiTimeTicks); + this->addNanoSec (ts.tv_nsec); +} + +// +// operator struct timeval () +// +epicsTime::operator struct timeval () const +{ + struct timeval ts; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + ts.tv_sec = ansiTimeTicks.ts; + ts.tv_usec = static_cast (this->nSec / nSecPerUSec); + return ts; +} + +// +// epicsTime (const struct timeval &ts) +// +epicsTime::epicsTime (const struct timeval &ts) +{ + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks.ts = ts.tv_sec; + *this = epicsTime (ansiTimeTicks); + this->addNanoSec (ts.tv_usec * nSecPerUSec); +} + +// +// operator aitTimeStamp () +// +epicsTime::operator aitTimeStamp () const +{ + aitTimeStamp ts; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + ts.tv_sec = ansiTimeTicks.ts; + ts.tv_nsec = this->nSec; + return ts; +} + +// +// epicsTime (const aitTimeStamp &ts) +// +epicsTime::epicsTime (const aitTimeStamp &ts) +{ + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks.ts = ts.tv_sec; + *this = epicsTime (ansiTimeTicks); + + unsigned long secAdj = ts.tv_nsec / nSecPerSec; + unsigned long nSecAdj = ts.tv_nsec % nSecPerSec; + *this = epicsTime (this->secPastEpoch+secAdj, this->nSec+nSecAdj); +} + +// +// epicsTime::ntpTimeStamp () +// +#ifdef NTP_SUPPORT +epicsTime::operator ntpTimeStamp () const +{ + ntpTimeStamp ts; + + if (lti.ntpEpochOffset>=0) { + unsigned long offset = static_cast (lti.ntpEpochOffset); + // underflow expected + ts.l_ui = this->secPastEpoch - offset; + } + else { + unsigned long offset = static_cast (-lti.ntpEpochOffset); + // overflow expected + ts.l_ui = this->secPastEpoch + offset; + } + + ts.l_uf = static_cast ( ( this->nSec * ULONG_MAX_PLUS_ONE ) / nSecPerSec ); + + return ts; +} +#endif + +// +// epicsTime::epicsTime (const ntpTimeStamp &ts) +// +#ifdef NTP_SUPPORT +epicsTime::epicsTime (const ntpTimeStamp &ts) +{ + + if (lti.ntpEpochOffset>=0) { + unsigned long offset = static_cast (lti.ntpEpochOffset); + // overflow expected + this->secPastEpoch = ts.l_ui + this->secPastEpoch + offset; + } + else { + unsigned long offset = static_cast (-lti.ntpEpochOffset); + // underflow expected + this->secPastEpoch = ts.l_ui + this->secPastEpoch - offset; + } + + this->nSec = static_cast ( ( ts.l_uf / ULONG_MAX_PLUS_ONE ) * nSecPerSec ); +} +#endif + +// +// size_t epicsTime::strftime (char *pBuff, size_t bufLength, const char *pFormat) +// +size_t epicsTime::strftime (char *pBuff, size_t bufLength, const char *pFormat) const +{ + // copy format (needs to be writable) + char format[256]; + strcpy (format, pFormat); + + // look for "%0f" at the end (used for fractional second formatting) + unsigned long fracWid; + char *fracPtr = fracFormat (format, &fracWid); + + // if found, hide from strftime + if (fracPtr != NULL) *fracPtr = '\0'; + + // format all but fractional seconds + tm_nano_sec tmns = *this; + size_t numChar = ::strftime (pBuff, bufLength, format, &tmns.ansi_tm); + if (numChar == 0 || fracPtr == NULL) return numChar; + + // check there are enough chars for the fractional seconds + if (numChar + fracWid < bufLength) + { + // divisors for fraction (see below) + const int div[9+1] = {(int)1e9,(int)1e8,(int)1e7,(int)1e6,(int)1e5, + (int)1e4,(int)1e3,(int)1e2,(int)1e1,(int)1e0}; + + // round and convert nanosecs to integer of correct range + int ndp = fracWid <= 9 ? fracWid : 9; + int frac = ((tmns.nSec + div[ndp]/2) % ((int)1e9)) / div[ndp]; + + // restore fractional format and format fraction + *fracPtr = '%'; + *(format + strlen (format) - 1) = 'u'; + sprintf (pBuff+numChar, fracPtr, frac); + } + return numChar + fracWid; +} + +// +// epicsTime::show (unsigned) +// +void epicsTime::show (unsigned) const +{ + char bigBuffer[256]; + + size_t numChar = strftime (bigBuffer, sizeof(bigBuffer), + "%a %b %d %Y %H:%M:%S.%09f"); + if (numChar>0) { + printf ("epicsTime: %s\n", bigBuffer); + } +} + +// +// epicsTime::operator + (const double &rhs) +// +// rhs has units seconds +// +epicsTime epicsTime::operator + (const double &rhs) const +{ + unsigned long newSec, newNSec, secOffset, nSecOffset; + double fnsec; + + if (rhs >= 0) { + secOffset = static_cast (rhs); + fnsec = rhs - secOffset; + nSecOffset = static_cast (fnsec * nSecPerSec); + + newSec = this->secPastEpoch + secOffset; // overflow expected + newNSec = this->nSec + nSecOffset; + if (newNSec >= nSecPerSec) { + newSec++; // overflow expected + newNSec -= nSecPerSec; + } + } + else { + secOffset = static_cast (-rhs); + fnsec = rhs + secOffset; + nSecOffset = static_cast (-fnsec * nSecPerSec); + + newSec = this->secPastEpoch - secOffset; // underflow expected + if (this->nSec>=nSecOffset) { + newNSec = this->nSec - nSecOffset; + } + else { + // borrow + newSec--; // underflow expected + newNSec = this->nSec + (nSecPerSec - nSecOffset); + } + } + return epicsTime (newSec, newNSec); +} + +// +// operator - +// +// To make this code robust during timestamp rollover events +// time stamp differences greater than one half full scale are +// interpreted as rollover situations: +// +// when RHS is greater than THIS: +// RHS-THIS > one half full scale => return THIS + (ULONG_MAX-RHS) +// RHS-THIS <= one half full scale => return -(RHS-THIS) +// +// when THIS is greater than or equal to RHS +// THIS-RHS > one half full scale => return -(RHS + (ULONG_MAX-THIS)) +// THIS-RHS <= one half full scale => return THIS-RHS +// +double epicsTime::operator - (const epicsTime &rhs) const +{ + double nSecRes, secRes; + + // + // first compute the difference between the nano-seconds members + // + // nano sec member is not allowed to be greater that 1/2 full scale + // so the unsigned to signed conversion is ok + // + if (this->nSec>=rhs.nSec) { + nSecRes = this->nSec - rhs.nSec; + } + else { + nSecRes = rhs.nSec - this->nSec; + nSecRes = -nSecRes; + } + + // + // next compute the difference between the seconds memebers + // and invert the sign of the nano seconds result if there + // is a range violation + // + if (this->secPastEpochsecPastEpoch; + if (secRes > ULONG_MAX/2) { + // + // In this situation where the difference is more than + // 68 years assume that the seconds counter has rolled + // over and compute the "wrap around" difference + // + secRes = 1 + (ULONG_MAX-secRes); + nSecRes = -nSecRes; + } + else { + secRes = -secRes; + } + } + else { + secRes = this->secPastEpoch - rhs.secPastEpoch; + if (secRes > ULONG_MAX/2) { + // + // In this situation where the difference is more than + // 68 years assume that the seconds counter has rolled + // over and compute the "wrap around" difference + // + secRes = 1 + (ULONG_MAX-secRes); + secRes = -secRes; + nSecRes = -nSecRes; + } + } + + return secRes + nSecRes/nSecPerSec; +} + +// +// operator <= +// +bool epicsTime::operator <= (const epicsTime &rhs) const +{ + bool rc; + + if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = true; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = false; + } + } + else if (this->secPastEpoch>rhs.secPastEpoch) { + if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = false; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = true; + } + } + else { + if (this->nSec<=rhs.nSec) { + rc = true; + } + else { + rc = false; + } + } + return rc; +} + +// +// operator < +// +bool epicsTime::operator < (const epicsTime &rhs) const +{ + bool rc; + + if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = true; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = false; + } + } + else if (this->secPastEpoch>rhs.secPastEpoch) { + if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = false; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = true; + } + } + else { + if (this->nSec epicsTime (*pRight); + } + epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) + { + return epicsTime (*pLeft) >= epicsTime (*pRight); + } + epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime (char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS) + { + return epicsTime(*pTS).strftime (pBuff, bufLength, pFormat); + } + epicsShareFunc void epicsShareAPI epicsTimeShow (const epicsTimeStamp *pTS, unsigned interestLevel) + { + epicsTime(*pTS).show (interestLevel); + } +} + +// +// utility routines +// + +// return pointer to trailing "%0f" ( is a number) in a format string, +// NULL if none; also return and a pointer to the "f" +static char *fracFormat (const char *pFormat, unsigned long *width) +{ + // initialize returned field width + *width = 0; + + // point at char past format string + const char *ptr = pFormat + strlen (pFormat); + + // if (last) char not 'f', no match + if (*(--ptr) != 'f') return NULL; + + // skip digits, extracting (must start with 0) + while (isdigit (*(--ptr))); // NB, empty loop body + ++ptr; // points to first digit, if any + if (*ptr != '0') return NULL; + if (sscanf (ptr, "%lu", width) != 1) return NULL; + + // if (prev) char not '%', no match + if (*(--ptr) != '%') return NULL; + + // return pointer to '%' + return (char *) ptr; +} diff --git a/src/libCom/osi/epicsTime.h b/src/libCom/osi/epicsTime.h new file mode 100644 index 000000000..8d7569d32 --- /dev/null +++ b/src/libCom/osi/epicsTime.h @@ -0,0 +1,361 @@ +/* epicsTime.h */ +/* Author Jeffrey O. Hill */ + +/********************COPYRIGHT NOTIFICATION********************************** +This software was developed under a United States Government license +described on the COPYRIGHT_Combined file included as part +of this distribution. +****************************************************************************/ +#ifndef epicsTimehInclude +#define epicsTimehInclude + +#include + +#include "shareLib.h" +#include "epicsTypes.h" +#include "epicsAssert.h" +#include "osdTime.h" +#ifdef __cplusplus +#include "locationException.h" +#endif /* __cplusplus */ + +/* epics time stamp for C interface*/ +typedef struct epicsTimeStamp { + epicsUInt32 secPastEpoch; /* seconds since 0000 Jan 1, 1990 */ + epicsUInt32 nsec; /* nanoseconds within second */ +} epicsTimeStamp; + +/*TS_STAMP is deprecated */ +#define TS_STAMP epicsTimeStamp + +struct timespec; /* POSIX real time */ +struct timeval; /* BSD */ + +/* Network Time Protocal Timestamp */ +struct ntpTimeStamp { + epicsUInt32 l_ui; /* sec past NTP epoch */ + epicsUInt32 l_uf; /* fractional seconds */ +}; + +#ifdef __cplusplus + +class aitTimeStamp; /* GDD*/ +// extended ANSI C RTL "struct tm" which includes nano seconds within a second. +struct tm_nano_sec { + struct tm ansi_tm; /* ANSI C time details */ + unsigned long nSec; /* nano seconds extension */ +}; + +// wrapping this in a struct allows conversion to and +// from ANSI time_t but does not allow unexpected +// conversions to occur +struct time_t_wrapper { + time_t ts; +}; + +class epicsTime; + +class epicsShareClass epicsTimeEvent +{ + friend class epicsTime; +public: + epicsTimeEvent (const int &eventName); +private: + unsigned eventNumber; +}; + +class epicsShareClass epicsTime +{ +public: + // exceptions + class unableToFetchCurrentTime {}; + class formatProblemWithStructTM {}; + + epicsTime (); + epicsTime (const epicsTime &t); + + static epicsTime getEvent (const epicsTimeEvent &event); + static epicsTime getCurrent (); + + // convert to and from EPICS epicsTimeStamp format + operator epicsTimeStamp () const; + epicsTime (const epicsTimeStamp &ts); + epicsTime operator = (const epicsTimeStamp &rhs); + + // convert to and from ANSI time_t + operator time_t_wrapper () const; + epicsTime (const time_t_wrapper &tv); + epicsTime operator = (const time_t_wrapper &rhs); + + // convert to and from ANSI C's "struct tm" (with nano seconds) + operator tm_nano_sec () const; + epicsTime (const tm_nano_sec &ts); + epicsTime operator = (const tm_nano_sec &rhs); + + // convert to and from POSIX RT's "struct timespec" + operator struct timespec () const; + epicsTime (const struct timespec &ts); + epicsTime operator = (const struct timespec &rhs); + + // convert to and from BSD's "struct timeval" + operator struct timeval () const; + epicsTime (const struct timeval &ts); + epicsTime operator = (const struct timeval &rhs); + + // convert to and from NTP timestamp format + operator ntpTimeStamp () const; + epicsTime (const ntpTimeStamp &ts); + epicsTime operator = (const ntpTimeStamp &rhs); + + // convert to and from GDD's aitTimeStamp format + operator aitTimeStamp () const; + epicsTime (const aitTimeStamp &ts); + epicsTime operator = (const aitTimeStamp &rhs); + + // arithmetic operators + double operator- (const epicsTime &rhs) const; // returns seconds + epicsTime operator+ (const double &rhs) const; // add rhs seconds + epicsTime operator- (const double &rhs) const; // subtract rhs seconds + epicsTime operator+= (const double &rhs); // add rhs seconds + epicsTime operator-= (const double &rhs); // subtract rhs seconds + + // comparison operators + bool operator == (const epicsTime &rhs) const; + bool operator != (const epicsTime &rhs) const; + bool operator <= (const epicsTime &rhs) const; + bool operator < (const epicsTime &rhs) const; + bool operator >= (const epicsTime &rhs) const; + bool operator > (const epicsTime &rhs) const; + + // convert current state to user-specified string + size_t strftime (char *pBuff, size_t bufLength, const char *pFormat) const; + + // dump current state to standard out + void show (unsigned interestLevel) const; + + // useful public constants + static const unsigned secPerMin; + static const unsigned mSecPerSec; + static const unsigned uSecPerSec; + static const unsigned nSecPerSec; + static const unsigned nSecPerUSec; + + // depricated + static void synchronize (); +private: + // private because: + // a) application does not break when EPICS epoch is changed + // b) no assumptions about internal storage or internal precision + // in the application + // c) it would be easy to forget which argument is nanoseconds + // and which argument is seconds (no help from compiler) + // + epicsTime (const unsigned long secPastEpoch, const unsigned long nSec); + void addNanoSec (long nanoSecAdjust); + + unsigned long secPastEpoch; // seconds since O000 Jan 1, 1990 + unsigned long nSec; // nanoseconds within second +}; + +extern "C" { +#endif /* __cplusplus */ + +/* All epicsTime routines return (-1,0) for (failure,success) */ +#define epicsTimeOK 0 +#define epicsTimeERROR (-1) + +#define epicsTimeEventCurrentTime 0 + +/* convert to and from ANSI C's "time_t" */ +epicsShareFunc int epicsShareAPI epicsTimeGetCurrent (epicsTimeStamp *pDest); +epicsShareFunc int epicsShareAPI epicsTimeGetEvent ( + epicsTimeStamp *pDest, unsigned eventNumber); + +/* convert to and from ANSI C's "struct tm" with nano second */ +epicsShareFunc int epicsShareAPI epicsTimeToTime_t ( + time_t *pDest, const epicsTimeStamp *pSrc); +epicsShareFunc int epicsShareAPI epicsTimeFromTime_t ( + epicsTimeStamp *pDest, time_t src); + +/*convert to and from ANSI C's "struct tm" with nano seconds */ +epicsShareFunc int epicsShareAPI epicsTimeToTM ( + struct tm *pDest, unsigned long *pNSecDest, const epicsTimeStamp *pSrc); +epicsShareFunc int epicsShareAPI epicsTimeFromTM ( + epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc); + +/* convert to and from POSIX RT's "struct timespec" */ +epicsShareFunc int epicsShareAPI epicsTimeToTimespec ( + struct timespec *pDest, const epicsTimeStamp *pSrc); +epicsShareFunc int epicsShareAPI epicsTimeFromTimespec ( + epicsTimeStamp *pDest, const struct timespec *pSrc); + +/* convert to and from BSD's "struct timeval" */ +epicsShareFunc int epicsShareAPI epicsTimeToTimeval ( + struct timeval *pDest, const epicsTimeStamp *pSrc); +epicsShareFunc int epicsShareAPI epicsTimeFromTimeval ( + epicsTimeStamp *pDest, const struct timeval *pSrc); + +/*arithmetic operations */ +epicsShareFunc double epicsShareAPI epicsTimeDiffInSeconds ( + const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight);/* left - right */ +epicsShareFunc void epicsShareAPI epicsTimeAddSeconds ( + epicsTimeStamp *pDest, double secondsToAdd); /* adds seconds to *pDest */ + +/*comparison operations: returns (0,1) if (false,true) */ +epicsShareFunc int epicsShareAPI epicsTimeEqual ( + const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight); +epicsShareFunc int epicsShareAPI epicsTimeNotEqual ( + const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight); +epicsShareFunc int epicsShareAPI epicsTimeLessThan ( + const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight); /*true if left < right */ +epicsShareFunc int epicsShareAPI epicsTimeLessThanEqual ( + const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight); /*true if left <= right) */ +epicsShareFunc int epicsShareAPI epicsTimeGreaterThan ( + const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight); /*true if left < right */ +epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual ( + const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight); /*true if left < right */ + +/*convert to ASCII string */ +epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime ( + char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS); + +/* dump current state to standard out */ +epicsShareFunc void epicsShareAPI epicsTimeShow ( + const epicsTimeStamp *, unsigned interestLevel); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* epicsTime inline member functions ,*/ +#ifdef __cplusplus + +inline epicsTime::epicsTime (const epicsTimeStamp &ts) +{ + this->secPastEpoch = ts.secPastEpoch; + this->nSec = ts.nsec; +} + +inline epicsTime epicsTime::getCurrent () +{ + epicsTimeStamp current; + int status; + + status = epicsTimeGetCurrent (¤t); + if (status) { + throwWithLocation ( unableToFetchCurrentTime () ); + } + + return epicsTime (current); +} + +inline epicsTime epicsTime::getEvent (const epicsTimeEvent &event) +{ + epicsTimeStamp current; + int status; + + status = epicsTimeGetEvent (¤t, event.eventNumber); + if (status) { + throwWithLocation ( unableToFetchCurrentTime () ); + } + + return epicsTime (current); +} + +inline void epicsTime::synchronize () {} // depricated + +inline epicsTime::epicsTime () : secPastEpoch(0u), nSec(0u) {} + +inline epicsTime::epicsTime (const epicsTime &t) +: secPastEpoch (t.secPastEpoch), nSec (t.nSec) {} + +inline epicsTime epicsTime::operator - (const double &rhs) const +{ + return epicsTime::operator + (-rhs); +} + +inline epicsTime epicsTime::operator += (const double &rhs) +{ + *this = epicsTime::operator + (rhs); + return *this; +} + +inline epicsTime epicsTime::operator -= (const double &rhs) +{ + *this = epicsTime::operator + (-rhs); + return *this; +} + +inline bool epicsTime::operator == (const epicsTime &rhs) const +{ + if (this->secPastEpoch == rhs.secPastEpoch && this->nSec == rhs.nSec) { + return true; + } + else { + return false; + } +} + +inline bool epicsTime::operator != (const epicsTime &rhs) const +{ + return !epicsTime::operator == (rhs); +} + +inline bool epicsTime::operator >= (const epicsTime &rhs) const +{ + return ! ( *this < rhs ); +} + +inline bool epicsTime::operator > (const epicsTime &rhs) const +{ + return ! ( *this <= rhs ); +} + +inline epicsTime epicsTime::operator = (const tm_nano_sec &rhs) +{ + return *this = epicsTime (rhs); +} + +inline epicsTime epicsTime::operator = (const struct timespec &rhs) +{ + *this = epicsTime (rhs); + return *this; +} + +inline epicsTime epicsTime::operator = (const aitTimeStamp &rhs) +{ + *this = epicsTime (rhs); + return *this; +} + +inline epicsTime epicsTime::operator = (const epicsTimeStamp &rhs) +{ + *this = epicsTime (rhs); + return *this; +} + +inline epicsTime::operator epicsTimeStamp () const +{ + epicsTimeStamp ts; + ts.secPastEpoch = this->secPastEpoch; + ts.nsec = this->nSec; + return ts; +} + +#ifdef NTP_SUPPORT +inline epicsTime epicsTime::operator = (const ntpTimeStamp &rhs) +{ + *this = epicsTime (rhs); + return *this; +} +#endif + +inline epicsTime epicsTime::operator = (const time_t_wrapper &rhs) +{ + *this = epicsTime (rhs); + return *this; +} +#endif /* __cplusplus */ + +#endif /* epicsTimehInclude */ +