| Classes | Job Modules | Data Objects | Services | Algorithms | Tools | Packages | Directories | Tracs |

In This Package:

TimeStamp.cc

Go to the documentation of this file.
00001 
00002 // $Id: TimeStamp.cc,v 1.1 2006/10/25 20:21:35 tagg Exp $
00003 //
00004 // The TimeStamp encapsulates the seconds and ns since EPOCH
00005 //
00006 // This extends (and isolates) struct timespec
00007 //    struct timespec
00008 //       {
00009 //          time_t   tv_sec;   /* seconds */
00010 //          long     tv_nsec;  /* nanoseconds */
00011 //       }
00012 //    time_t seconds is relative to Jan 1, 1970 00:00:00 UTC
00013 //
00014 // Due to ROOT/CINT limitations TimeStamp does not explicitly
00015 // hold a timespec struct; attempting to do so means the Streamer
00016 // must be hand written.  Instead we have chosen to simply contain
00017 // similar fields within the private area of this class.
00018 //
00019 // NOTE: the use of time_t (and its default implementation as a 32 int)
00020 //       implies overflow conditions occurs somewhere around
00021 //       Jan 18, 19:14:07, 2038.
00022 //       If this experiment is still going when it becomes significant
00023 //       someone will have to deal with it.
00024 //
00025 // Author:  R. Hatcher 2000.04.19
00026 //          R. Hatcher 2000.12.20 -- convert from TDatime to struct timespec
00027 //          bv@bnl.gov 2008/3/7 - remove ROOT dependency and assoc fluff
00028 //
00030 
00031 #include "Context/TimeStamp.h"
00032 #include <iostream>
00033 #include <cmath>
00034 #include <string>
00035 #include <algorithm>
00036 #include <limits.h>
00037 #include <cstring>
00038 #include <cstdio>
00039 
00040 const int kNsPerSec = 1000000000;
00041 
00042 //_____________________________________________________________________________
00043 std::ostream& operator<<(std::ostream& os, const TimeStamp& ts) 
00044 {
00045     if (os.good()) {
00046         if (os.tie()) os.tie()->flush(); // instead of opfx
00047         os << ts.AsString("c");
00048     }
00049     // instead of os.osfx()
00050     if (os.flags() & std::ios::unitbuf) os.flush();
00051     return os;
00052 }
00053 
00054 TimeStamp TimeStamp::GetBOT()
00055 {
00056     return TimeStamp((time_t)0,0);
00057 }
00058 
00059 TimeStamp TimeStamp::GetEOT()
00060 {
00061     return TimeStamp((time_t)INT_MAX,0);
00062 }
00063 
00064 TimeStamp TimeStamp::GetNBOT()
00065 {
00066     return TimeStamp((time_t)INT_MIN,0);
00067 }
00068 
00069 
00070 //_____________________________________________________________________________
00071 // don't put these in the header or using the include file w/ CINT
00072 // become problematic
00073 
00074 // default ctor sets it value to current time (as best possible)
00075 TimeStamp::TimeStamp() { Set(); }
00076 TimeStamp::~TimeStamp() { ; }
00077 
00078 //_____________________________________________________________________________
00079 TimeStamp::TimeStamp(unsigned int year, unsigned int month,
00080                      unsigned int day,  unsigned int hour,
00081                      unsigned int min,  unsigned int sec,
00082                      unsigned int nsec, 
00083                      bool isUTC, int secOffset)
00084 {
00085     // Create a TimeStamp and set it to the specified year, month,
00086     // day, time, hour, minute, second and nanosec.
00087     // If !isUTC then it is assumed to be the standard local time zone.
00088     //
00089     // If local time is PST then one can use
00090     //    TimeStamp(year,month,day,hour,min,sec,nsec,kFALSE,0);
00091     // or
00092     //    int secOffset = 8*60*60;
00093     //    TimeStamp(year,month,day,hour,min,sec,nsec,kTRUE,8*60*60);
00094    
00095     Set(year, month, day, hour, min, sec, nsec, isUTC, secOffset);
00096 }
00097 
00098 //_____________________________________________________________________________
00099 TimeStamp::TimeStamp(unsigned int date, unsigned int time, unsigned int nsec, 
00100                      bool isUTC, int secOffset)
00101 {
00102     // Create a TimeStamp and set it to the specified date, time, nanosec.
00103     // If !isUTC then it is assumed to be the standard local time zone.
00104 
00105     Set(date, time, nsec, isUTC, secOffset);
00106 }
00107 
00108 //_____________________________________________________________________________
00109 const char *TimeStamp::AsString(const char* option) const
00110 {
00111     // Return the date & time as a string.
00112     //
00113     // Result is pointer to a statically allocated string.
00114     // User should copy this into their own buffer before calling
00115     // this method again.  This is somewhat mitigated
00116     // by use of a circular buffer of strings.
00117     //
00118     // Option "l" returns it in local zone format
00119     // (can be applied to default or compact format).
00120     //
00121     // Default format is RFC822 compliant:
00122     //   "Mon, 02 Jan 2001 18:11:12 +0000 (GMT) +999999999 nsec"
00123     //   "Mon, 02 Jan 2001 10:11:12 -0800 (PST) +999999999 nsec"
00124     //
00125     // Option "c" compact is (almost) ISO 8601 compliant:
00126     //   "2001-01-02 18:11:12.9999999999Z"
00127     //   "2001-01-02 10:11:12.9999999999-0800"  if PST
00128     //      * uses "-" as date separator as specified in ISO 8601
00129     //      * uses "." rather than preferred "," for decimal separator
00130     //      * -HHMM is the difference between local and UTC (if behind, + if ahead).
00131     //   The "-HHMM" is replaced with "Z" if given as UTC.
00132     //   To be strictly conforming it should use "T" instead of the
00133     //   blank separating the date and time.
00134     //
00135     // Option "2" returns as {sec,nsec} integers.
00136     //
00137     // Option "s" returns "2001-01-02 18:11:12" with an implied UTC,
00138     // overrides "l" option.
00139 
00140     // Internally uses a circular list of buffers to avoid problems
00141     // using AsString multiple times in a single statement.
00142 
00143     const int nbuffers = 8;     // # of buffers
00144 
00145     static char formatted[nbuffers][64];  // strftime fields substituted
00146     static char formatted2[nbuffers][64]; // nanosec field substituted
00147     static int ibuffer = nbuffers;
00148     ibuffer = (ibuffer+1)%nbuffers; // each call moves to next buffer
00149 
00150     std::string opt = option;
00151     std::transform(opt.begin(),opt.end(),opt.begin(),::tolower);
00152 
00153     if (opt.find("2") != std::string::npos) {
00154         // return string formatted as integer {sec,nsec}
00155         sprintf(formatted[ibuffer], "{%d,%d}", mSec, mNanoSec);
00156         return formatted[ibuffer];
00157     }
00158 
00159 #ifdef linux
00160     // under linux %z is the hour offset and %Z is the timezone name
00161     const char *RFC822   = "%a, %d %b %Y %H:%M:%S %z (%Z) +#9ld nsec";
00162     const char *ISO8601  = "%Y-%m-%d %H:%M:%S.#9.9ld%z";
00163     const char *ISO8601Z = "%Y-%m-%d %H:%M:%S.#9.9ldZ";
00164 #else
00165     // otherwise only %Z is guarenteed to be defind
00166     const char *RFC822   = "%a, %d %b %Y %H:%M:%S %Z +#9ld nsec";
00167     const char *ISO8601  = "%Y-%m-%d %H:%M:%S.#9.9ld%Z";
00168     const char *ISO8601Z = "%Y-%m-%d %H:%M:%S.#9.9ldZ";
00169 #endif
00170     const char *SQL = "%Y-%m-%d %H:%M:%S";
00171 
00172     bool asLocal = (opt.find("l") != std::string::npos);
00173     bool asSQL   = (opt.find("s") != std::string::npos);
00174     if (asSQL) asLocal = false;
00175 
00176     const char *format = RFC822;
00177     if (opt.find("c") != std::string::npos) {
00178         format = ISO8601;
00179         if (!asLocal) format = ISO8601Z;
00180     }
00181     if (asSQL) format = SQL;
00182 
00183     struct tm *ptm;
00184     time_t seconds = (time_t) mSec;   // deal with possible mismatch of types 
00185     // of mSec and the time_t required 
00186     // by functions
00187 
00188     // get the components into a tm struct
00189     ptm = (asLocal) ? localtime(&seconds) : gmtime(&seconds);
00190 
00191     // format all but the nsec field
00192     // size_t length =
00193     strftime(formatted[ibuffer], sizeof(formatted[ibuffer]), format, ptm);
00194 
00195     if (asSQL) return formatted[ibuffer];
00196 
00197     // hack in the nsec part
00198     char *ptr = strrchr(formatted[ibuffer], '#');
00199     if (ptr) *ptr = '%';    // substitute % for #
00200     sprintf(formatted2[ibuffer], formatted[ibuffer], mNanoSec);
00201 
00202     return formatted2[ibuffer];
00203 }
00204 
00205 //_____________________________________________________________________________
00206 void TimeStamp::Copy(TimeStamp &ts) const
00207 {
00208     // Copy this to ts.
00209 
00210     ts.mSec     = mSec;
00211     ts.mNanoSec = mNanoSec;
00212 
00213 }
00214 
00215 //_____________________________________________________________________________
00216 int TimeStamp::GetDate(bool inUTC, int secOffset,
00217                        unsigned int* year, unsigned int* month, unsigned int* day) const
00218 {
00219     // Return date in form of 19971224 (i.e. 24/12/1997),
00220     // if non-zero pointers supplied for year, month, day fill those as well
00221 
00222     time_t atime = mSec + secOffset;
00223     struct tm *ptm = (inUTC) ? gmtime(&atime) : localtime(&atime);
00224 
00225     if (year)  *year  = ptm->tm_year + 1900;
00226     if (month) *month = ptm->tm_mon + 1;
00227     if (day)   *day   = ptm->tm_mday;
00228 
00229     return (1900+ptm->tm_year)*10000 + (1+ptm->tm_mon)*100 + ptm->tm_mday;
00230 
00231 }
00232 
00233 //_____________________________________________________________________________
00234 int TimeStamp::GetTime(bool inUTC, int secOffset,
00235                        unsigned int* hour, unsigned int* min, unsigned int* sec) const
00236 {
00237     // Return time in form of 123623 (i.e. 12:36:23),
00238     // if non-zero pointers supplied for hour, min, sec fill those as well
00239 
00240     time_t atime = mSec + secOffset;
00241     struct tm *ptm = (inUTC) ? gmtime(&atime) : localtime(&atime);
00242 
00243     if (hour) *hour = ptm->tm_hour;
00244     if (min)  *min  = ptm->tm_min;
00245     if (sec)  *sec  = ptm->tm_sec;
00246 
00247     return ptm->tm_hour*10000 + ptm->tm_min*100 + ptm->tm_sec;
00248 
00249 }
00250 
00251 //_____________________________________________________________________________
00252 int TimeStamp::GetZoneOffset()
00253 {
00254     // Static method returning local (current) time zone offset from UTC.
00255     // This is the difference in seconds between UTC and local standard time.
00256 
00257     // ?? should tzset (_tzset) be called?
00258 #ifndef R__WIN32
00259     tzset();
00260 #if !defined(R__FBSD) && !defined(__APPLE__)
00261     return  timezone;   /* unix has extern long int */
00262 #else
00263     time_t *tp = 0;
00264     time(tp);
00265     return localtime(tp)->tm_gmtoff;
00266 #endif
00267 #else
00268     _tzset();
00269     return _timezone;   /* Win32 prepends "_" */
00270 #endif
00271 }
00272 
00273 //_____________________________________________________________________________
00274 void TimeStamp::Add(const TimeStamp &offset)
00275 {
00276     // Add "offset" as a delta time.
00277 
00278     mSec     += offset.mSec;
00279     mNanoSec += offset.mNanoSec;
00280     NormalizeNanoSec();
00281 
00282 }
00283 
00284 void TimeStamp::Add(double seconds)
00285 {
00286     // Add 'seconds' as a delta time
00287 
00288     mSec += (int) seconds;
00289     mNanoSec += (int) (fmod(seconds,1.0) * 1e9);
00290     NormalizeNanoSec();
00291     if(seconds > 1e6) 
00292         std::cerr << "TimeStamp moved by offset " << seconds <<" which is too large to maintain ns accuracy." << std::endl;
00293 }
00294 
00295 void TimeStamp::Subtract(const TimeStamp &offset)
00296 {
00297     // Subtract "offset" as a delta time.
00298 
00299     mSec     -= offset.mSec;
00300     mNanoSec -= offset.mNanoSec;
00301     NormalizeNanoSec();
00302 
00303 }
00304 
00305 void TimeStamp::Subtract(double seconds)
00306 {
00307     // Subtract 'seconds' as a delta time
00308 
00309     mSec -= (int) seconds;
00310     mNanoSec -= (int) (fmod(seconds,1.0) * 1e9);
00311     NormalizeNanoSec();
00312     if(seconds > 1e6) 
00313         std::cerr << "TimeStamp moved by offset " << seconds <<" which is too large to maintain ns accuracy." << std::endl;
00314 }
00315 
00316 
00317 //_____________________________________________________________________________
00318 void TimeStamp::Print(const char* option) const
00319 {
00320     // Print date and time.
00321 
00322     printf("Date/Time = %s\n", AsString(option));
00323 
00324 }
00325 
00326 #include <sys/time.h>           // for gettimeofday
00327 
00328 //_____________________________________________________________________________
00329 void TimeStamp::Set()
00330 {
00331     // Set Date/Time to current time as reported by the system.
00332     // no accounting for nanoseconds with std ANSI functions,
00333     // ns part faked so that subsequent calls simply add 1 to it
00334     // this ensures that calls within the same second come back
00335     // distinct (and sortable).
00336 
00337     // this should work on UNIX to get microsec precision
00338     // we'll stick to a ns hack to make calls unique
00339     struct timeval now;
00340     if (!gettimeofday(&now,0)) {
00341         mSec     = now.tv_sec;
00342         mNanoSec = now.tv_usec * 1000;
00343     }
00344     else {
00345         time_t nowtime;
00346         time(&nowtime);
00347         mSec     = nowtime;
00348         mNanoSec = 0;
00349     }
00350 
00351     static int sec = 0, nsec = 0, fake_ns = 0;
00352 
00353     if (mSec == sec && mNanoSec == nsec)
00354         mNanoSec += ++fake_ns;
00355     else {
00356         fake_ns = 0;
00357         sec     = mSec;
00358         nsec    = mNanoSec;
00359     }
00360 
00361 }
00362 
00363 //_____________________________________________________________________________
00364 void TimeStamp::Set(int year, int month, int day,
00365                     int hour, int min, int sec, 
00366                     int nsec, bool isUTC, int secOffset)
00367 {
00368     // Set Date/Time from components.
00369     // 
00370     // month & day both use normal 1..12 and 1..31 counting
00371     // hours, min, sec run from 0 to 23, 59, 59 respectively;
00372     // secOffset provides method for adjusting for alternative timezones
00373     //
00374     // "year"  |    0    1 ... 37 | 38...69   |   70 .. 100  101 ..  137
00375     // true    | 2000 2001   2037 | undefined | 1970   2000 2001 .. 2037
00376     //
00377     // "year"  | 138...1969 | 1970 .. 2037 | ...
00378     // true    | undefined  | 1970 .. 2037 | undefined
00379     //
00380 
00381 
00382     // deal with special formats of year
00383     if (year <= 37)                year += 2000;
00384     if (year >= 70 && year <= 137) year += 1900;
00385     // tm.tm_year is years since 1900
00386     if (year >= 1900)              year -= 1900;
00387 
00388     struct tm tmstruct;
00389     tmstruct.tm_year  = year;    // years since 1900
00390     tmstruct.tm_mon   = month-1; // months since Jan [0,11]
00391     tmstruct.tm_mday  = day;     // day of the month [1,31]
00392     tmstruct.tm_hour  = hour;    // hours since midnight [0,23]
00393     tmstruct.tm_min   = min;     // minutes after the hour [0,59]
00394     tmstruct.tm_sec   = sec + secOffset;  // seconds after the minute [0,59]
00395     tmstruct.tm_isdst = -1;     // let "mktime" determine DST setting
00396 
00397     const time_t bad_time_t = (time_t) -1;
00398     // convert tm struct to time_t, if values are given in UTC then
00399     // no standard routine exists and we'll have to use our homegrown routine,
00400     // if values are given in local time then use "mktime"
00401     // which also normalizes the tm struct as a byproduct
00402     time_t utc_sec = (isUTC) ? MktimeFromUTC(&tmstruct) : mktime(&tmstruct);
00403  
00404     //   TimeStamp::Dump_tm_struct(tmstruct);
00405 
00406     if (utc_sec == bad_time_t)
00407         std::cerr << "TimeStamp::Set mktime returned -1" << std::endl;
00408 
00409     mSec  = utc_sec;
00410     mNanoSec = nsec;
00411 
00412     NormalizeNanoSec();
00413 }
00414 
00415 //_____________________________________________________________________________
00416 void TimeStamp::Set(int date, int time, int nsec,
00417                     bool isUTC, int secOffset)
00418 {
00419     // Set date/time from integers of the form [yy]YYMMDD and HHMMSS,
00420     // assume UTC (UTC) components:
00421     //
00422     //  MM: 01=January .. 12=December
00423     //  DD: 01 .. 31
00424     //
00425     //  HH: 00=midnight .. 23
00426     //  MM: 00 .. 59
00427     //  SS: 00 .. 69
00428     //
00429     // Date must be in format 980418 or 19980418
00430     //                       1001127 or 20001127  (i.e. year 100 = 2000).
00431     // Time must be in format 224512 (second precision).
00432     // Date must be >= 700101.
00433 
00434     int year  = date/10000;
00435     int month = (date-year*10000)/100;
00436     int day   = date%100;
00437 
00438     // protect against odd attempts at time offsets
00439     const int oneday = 240000;
00440     while (time < 0) {
00441         time += oneday;
00442         day  -= 1;
00443     }
00444     while (time > oneday) {
00445         time -= oneday;
00446         day  += 1;
00447     }
00448     int hour  = time/10000;
00449     int min   = (time-hour*10000)/100;
00450     int sec   = time%100;
00451 
00452     Set(year, month, day, hour, min, sec, nsec, isUTC, secOffset);
00453 
00454 }
00455 
00456 //_____________________________________________________________________________
00457 void TimeStamp::NormalizeNanoSec()
00458 {
00459     // Ensure that the mNanoSec field is in range [0,99999999].
00460 
00461     // deal with negative values
00462     while (mNanoSec < 0) {
00463         mNanoSec += kNsPerSec;
00464         mSec -= 1;
00465     }
00466     // deal with values inf mNanoSec greater than one sec
00467     while (mNanoSec >= kNsPerSec) {
00468         mNanoSec -= kNsPerSec;
00469         mSec += 1;
00470     }
00471 }
00472 //_____________________________________________________________________________
00473 time_t TimeStamp::MktimeFromUTC(tm *tmstruct)
00474 {
00475     // Equivalent of standard routine "mktime" but
00476     // using the assumption that tm struct is filled with UTC, not local, time.
00477 
00478     // This version *ISN'T* configured to handle every possible
00479     // weirdness of out-of-range values in the case of normalizing
00480     // the tm struct.
00481 
00482     // This version *DOESN'T* correctly handle values that can't be
00483     // fit into a time_t (i.e. beyond year 2038-01-18 19:14:07, or
00484     // before the start of Epoch).
00485 
00486     const int days[]     = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
00487     const int daysLeap[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
00488 
00489     int year = tmstruct->tm_year + 1900;
00490     bool isleap = TimeStamp::IsLeapYear(year);
00491 
00492     const int *daysInMonth = days;
00493     if (isleap) daysInMonth = daysLeap;
00494 
00495     // fill in tmstruct->tm_yday
00496 
00497     int &ref_tm_mon = tmstruct->tm_mon;
00498     int &ref_tm_mday = tmstruct->tm_mday;
00499     // count days in months past
00500     tmstruct->tm_yday = 0;
00501     for (int imonth = 0; imonth < ref_tm_mon; imonth++) {
00502         tmstruct->tm_yday += daysInMonth[imonth];
00503     }
00504     tmstruct->tm_yday += ref_tm_mday - 1;  // day [1-31] but yday [0-365]
00505 
00506     // adjust if day in this month is more than the month has
00507     while (ref_tm_mday > daysInMonth[ref_tm_mon]) {
00508         ref_tm_mday -= daysInMonth[ref_tm_mon];
00509         ref_tm_mon++;
00510     }
00511 
00512     // *should* calculate tm_wday (0-6) here ...
00513 
00514     // UTC is never DST
00515     tmstruct->tm_isdst = 0;
00516 
00517     // Calculate seconds since the Epoch based on formula in
00518     // POSIX  IEEEE Std 1003.1b-1993 pg 22
00519 
00520     int utc_sec = tmstruct->tm_sec +
00521         tmstruct->tm_min*60 +
00522         tmstruct->tm_hour*3600 +
00523         tmstruct->tm_yday*86400 +
00524         (tmstruct->tm_year-70)*31536000 +
00525         ((tmstruct->tm_year-69)/4)*86400;
00526 
00527     return utc_sec;
00528 }
00529 
00530 //_____________________________________________________________________________
00531 bool TimeStamp::IsLeapYear(int year)
00532 {
00533     // Is the given year a leap year.
00534 
00535 
00536     // The calendar year is 365 days long, unless the year is exactly divisible
00537     // by 4, in which case an extra day is added to February to make the year
00538     // 366 days long. If the year is the last year of a century, eg. 1700, 1800,
00539     // 1900, 2000, then it is only a leap year if it is exactly divisible by
00540     // 400. Therefore, 1900 wasn't a leap year but 2000 was. The reason for
00541     // these rules is to bring the average length of the calendar year into
00542     // line with the length of the Earth's orbit around the Sun, so that the
00543     // seasons always occur during the same months each year.
00544 
00545     if (year%4 != 0) {
00546         return false;
00547     }
00548     else {
00549         if (year%400 == 0) {
00550             return true;
00551         }
00552         else { 
00553             if (year%100 == 0) {
00554                 return false;
00555             }
00556             else {
00557                 return true;
00558             }
00559         }
00560     }
00561 
00562 }
00563 
00564 //_____________________________________________________________________________
00565 void TimeStamp::DumpTMStruct(const tm &tmstruct) 
00566 {
00567     // Print out the "tm" structure:
00568     // tmstruct.tm_year = year;    // years since 1900
00569     // tmstruct.tm_mon  = month-1; // months since Jan [0,11]
00570     // tmstruct.tm_mday = day;     // day of the month [1,31]
00571     // tmstruct.tm_hour = hour;    // hours since midnight [0,23]
00572     // tmstruct.tm_min  = min;     // minutes after the hour [0,59]
00573     // tmstruct.tm_sec  = sec;     // seconds after the minute [0,59]
00574     // tmstruct.tm_wday            // day of week [0,6]
00575     // tmstruct.tm_yday            // days in year [0,365]
00576     // tmstruct.tm_isdst           // DST [-1/0/1]  (unknown,false,true)
00577 
00578     printf(" tm { year %4d, mon   %2d, day   %2d,\n",
00579            tmstruct.tm_year,
00580            tmstruct.tm_mon,
00581            tmstruct.tm_mday);
00582     printf("      hour   %2d, min   %2d, sec   %2d,\n",
00583            tmstruct.tm_hour,
00584            tmstruct.tm_min,
00585            tmstruct.tm_sec);
00586     printf("      wday   %2d, yday %3d, isdst %2d",
00587            tmstruct.tm_wday,
00588            tmstruct.tm_yday,
00589            tmstruct.tm_isdst);
00590 #ifdef linux
00591     printf(",\n      tm_gmtoff %7ld,  tm_zone \"%s\"",
00592 #ifdef __USE_BSD
00593            tmstruct.tm_gmtoff,tmstruct.tm_zone);
00594 #else
00595     tmstruct.__tm_gmtoff,tmstruct.__tm_zone);
00596 #endif
00597 #endif
00598     printf("}\n");
00599 }
00600 //_____________________________________________________________________________
| Classes | Job Modules | Data Objects | Services | Algorithms | Tools | Packages | Directories | Tracs |

Generated on Mon Apr 11 20:15:31 2011 for Context by doxygen 1.4.7