EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ValTimeStamp.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file ValTimeStamp.cxx
1 
2 #include "ValTimeStamp.h"
3 #include <climits>
4 #include <math.h>
5 #include <unistd.h>
6 #include <sys/time.h>
7 
8 #include "TString.h"
9 
11 
12 const Int_t kNsPerSec = 1000000000;
13 
14 //_____________________________________________________________________________
15 std::ostream& operator<<(std::ostream& os, const ValTimeStamp& ts)
16 {
17  if (os.good()) {
18  if (os.tie()) { os.tie()->flush(); } // instead of opfx
19  os << ts.AsString("c");
20  }
21  // instead of os.osfx()
22  if (os.flags() & std::ios::unitbuf) { os.flush(); }
23  return os;
24 }
25 
27 {
28  return ValTimeStamp((time_t)0,0);
29 }
30 
32 {
33  return ValTimeStamp((time_t)INT_MAX,0);
34 }
35 
37 {
38  return ValTimeStamp((time_t)INT_MIN,0);
39 }
40 
41 
42 //_____________________________________________________________________________
43 // don't put these in the header or using the include file w/ CINT
44 // become problematic
45 
46 // default ctor sets it value to current time (as best possible)
47 ValTimeStamp::ValTimeStamp() : fSec(0), fNanoSec(0) { Set(); }
49 
50 //_____________________________________________________________________________
51 ValTimeStamp::ValTimeStamp(UInt_t year, UInt_t month,
52  UInt_t day, UInt_t hour,
53  UInt_t min, UInt_t sec,
54  UInt_t nsec,
55  Bool_t isUTC, Int_t secOffset)
56  : fSec(0), fNanoSec(0)
57 {
58  // Create a ValTimeStamp and set it to the specified year, month,
59  // day, time, hour, minute, second and nanosec.
60  // If !isUTC then it is assumed to be the standard local time zone.
61  //
62  // If local time is PST then one can use
63  // ValTimeStamp(year,month,day,hour,min,sec,nsec,kFALSE,0);
64  // or
65  // Int_t secOffset = 8*60*60;
66  // ValTimeStamp(year,month,day,hour,min,sec,nsec,kTRUE,8*60*60);
67 
68  Set(year, month, day, hour, min, sec, nsec, isUTC, secOffset);
69 }
70 
71 //_____________________________________________________________________________
72 ValTimeStamp::ValTimeStamp(UInt_t date, UInt_t time, UInt_t nsec,
73  Bool_t isUTC, Int_t secOffset)
74  : fSec(0), fNanoSec(0)
75 {
76  // Create a ValTimeStamp and set it to the specified date, time, nanosec.
77  // If !isUTC then it is assumed to be the standard local time zone.
78 
79  Set(date, time, nsec, isUTC, secOffset);
80 }
81 
82 //_____________________________________________________________________________
83 const char* ValTimeStamp::AsString(Option_t* option) const
84 {
85  // Return the date & time as a string.
86  //
87  // Result is pointer to a statically allocated string.
88  // User should copy this into their own buffer before calling
89  // this method again. This is somewhat mitigated
90  // by use of a circular buffer of strings.
91  //
92  // Option "l" returns it in local zone format
93  // (can be applied to default or compact format).
94  //
95  // Default format is RFC822 compliant:
96  // "Mon, 02 Jan 2001 18:11:12 +0000 (GMT) +999999999 nsec"
97  // "Mon, 02 Jan 2001 10:11:12 -0800 (PST) +999999999 nsec"
98  //
99  // Option "c" compact is (almost) ISO 8601 compliant:
100  // "2001-01-02 18:11:12.9999999999Z"
101  // "2001-01-02 10:11:12.9999999999-0800" if PST
102  // * uses "-" as date separator as specified in ISO 8601
103  // * uses "." rather than preferred "," for decimal separator
104  // * -HHMM is the difference between local and UTC (if behind, + if ahead).
105  // The "-HHMM" is replaced with "Z" if given as UTC.
106  // To be strictly conforming it should use "T" instead of the
107  // blank separating the date and time.
108  //
109  // Option "2" returns as {sec,nsec} integers.
110  //
111  // Option "s" returns "2001-01-02 18:11:12" with an implied UTC,
112  // overrides "l" option.
113 
114  // Internally uses a circular list of buffers to avoid problems
115  // using AsString multiple times in a single statement.
116 
117  const int nbuffers = 8; // # of buffers
118 
119  static char formatted[nbuffers][64]; // strftime fields substituted
120  static char formatted2[nbuffers][64]; // nanosec field substituted
121  static int ibuffer = nbuffers;
122  ibuffer = (ibuffer+1)%nbuffers; // each call moves to next buffer
123 
124  TString opt = option;
125  opt.ToLower();
126 
127  if (opt.Contains("2")) {
128  // return string formatted as integer {sec,nsec}
129  sprintf(formatted[ibuffer], "{%d,%d}", fSec, fNanoSec);
130  return formatted[ibuffer];
131  }
132 
133 #ifdef linux
134  // under linux %z is the hour offset and %Z is the timezone name
135  const char* RFC822 = "%a, %d %b %Y %H:%M:%S %z (%Z) +#9ld nsec";
136  const char* ISO8601 = "%Y-%m-%d %H:%M:%S.#9.9ld%z";
137  const char* ISO8601Z = "%Y-%m-%d %H:%M:%S.#9.9ldZ";
138 #else
139  // otherwise only %Z is guarenteed to be defind
140  const char* RFC822 = "%a, %d %b %Y %H:%M:%S %Z +#9ld nsec";
141  const char* ISO8601 = "%Y-%m-%d %H:%M:%S.#9.9ld%Z";
142  const char* ISO8601Z = "%Y-%m-%d %H:%M:%S.#9.9ldZ";
143 #endif
144  const char* SQL = "%Y-%m-%d %H:%M:%S";
145 
146  Bool_t asLocal = opt.Contains("l");
147  Bool_t asSQL = opt.Contains("s");
148  if (asSQL) { asLocal = kFALSE; }
149 
150  const char* format = RFC822;
151  if (opt.Contains("c")) {
152  format = ISO8601;
153  if (!asLocal) { format = ISO8601Z; }
154  }
155  if (asSQL) { format = SQL; }
156 
157  struct tm* ptm;
158  time_t seconds = (time_t) fSec; // deal with possible mismatch of types
159  // of fSec and the time_t required
160  // by functions
161 
162  // get the components into a tm struct
163  ptm = (asLocal) ? localtime(&seconds) : gmtime(&seconds);
164 
165  // format all but the nsec field
166  // size_t length =
167  strftime(formatted[ibuffer], sizeof(formatted[ibuffer]), format, ptm);
168 
169  if (asSQL) { return formatted[ibuffer]; }
170 
171  // hack in the nsec part
172  char* ptr = strrchr(formatted[ibuffer], '#');
173  if (ptr) { *ptr = '%'; } // substitute % for #
174  sprintf(formatted2[ibuffer], formatted[ibuffer], fNanoSec);
175 
176  return formatted2[ibuffer];
177 }
178 
179 //_____________________________________________________________________________
181 {
182  // Copy this to ts.
183 
184  ts.fSec = fSec;
185  ts.fNanoSec = fNanoSec;
186 
187 }
188 
189 //_____________________________________________________________________________
190 Int_t ValTimeStamp::GetDate(Bool_t inUTC, Int_t secOffset,
191  UInt_t* year, UInt_t* month, UInt_t* day) const
192 {
193  // Return date in form of 19971224 (i.e. 24/12/1997),
194  // if non-zero pointers supplied for year, month, day fill those as well
195 
196  time_t atime = fSec + secOffset;
197  struct tm* ptm = (inUTC) ? gmtime(&atime) : localtime(&atime);
198 
199  if (year) { *year = ptm->tm_year + 1900; }
200  if (month) { *month = ptm->tm_mon + 1; }
201  if (day) { *day = ptm->tm_mday; }
202 
203  return (1900+ptm->tm_year)*10000 + (1+ptm->tm_mon)*100 + ptm->tm_mday;
204 
205 }
206 
207 //_____________________________________________________________________________
208 Int_t ValTimeStamp::GetTime(Bool_t inUTC, Int_t secOffset,
209  UInt_t* hour, UInt_t* min, UInt_t* sec) const
210 {
211  // Return time in form of 123623 (i.e. 12:36:23),
212  // if non-zero pointers supplied for hour, min, sec fill those as well
213 
214  time_t atime = fSec + secOffset;
215  struct tm* ptm = (inUTC) ? gmtime(&atime) : localtime(&atime);
216 
217  if (hour) { *hour = ptm->tm_hour; }
218  if (min) { *min = ptm->tm_min; }
219  if (sec) { *sec = ptm->tm_sec; }
220 
221  return ptm->tm_hour*10000 + ptm->tm_min*100 + ptm->tm_sec;
222 
223 }
224 
225 //_____________________________________________________________________________
227 {
228  // Static method returning local (current) time zone offset from UTC.
229  // This is the difference in seconds between UTC and local standard time.
230 
231  // ?? should tzset (_tzset) be called?
232 #ifndef R__WIN32
233  tzset();
234 #if !defined(R__MACOSX) && !defined(R__FBSD)
235  return timezone; /* unix has extern long int */
236 #else
237  time_t* tp = 0;
238  time(tp);
239  return localtime(tp)->tm_gmtoff;
240 #endif
241 #else
242  _tzset();
243  return _timezone; /* Win32 prepends "_" */
244 #endif
245 }
246 
247 //_____________________________________________________________________________
249 {
250  // Add "offset" as a delta time.
251 
252  fSec += offset.fSec;
253  fNanoSec += offset.fNanoSec;
255 
256 }
257 
258 void ValTimeStamp::Add(Double_t seconds)
259 {
260  // Add 'seconds' as a delta time
261 
262  fSec += (Int_t) seconds;
263  fNanoSec += (Int_t) (fmod(seconds,1.0) * 1e9);
265  if(seconds > 1e6)
266  cout <<"-I- ValTimeStamp::Add(): "
267  << " ValTimeStamp moved by offset "
268  << seconds <<" which is too large to maintain ns accuracy."
269  << endl;
270 }
271 
272 //_____________________________________________________________________________
273 void ValTimeStamp::Print(Option_t* option) const
274 {
275  // Print date and time.
276 
277  printf("Date/Time = %s\n", AsString(option));
278 
279 }
280 
281 //_____________________________________________________________________________
283 {
284  // Set Date/Time to current time as reported by the system.
285  // no accounting for nanoseconds with std ANSI functions,
286  // ns part faked so that subsequent calls simply add 1 to it
287  // this ensures that calls within the same second come back
288  // distinct (and sortable).
289 
290 #ifdef R__WIN32
291  ULARGE_INTEGER time;
292  GetSystemTimeAsFileTime((FILETIME*)&time);
293  // NT keeps time in FILETIME format which is 100ns ticks since
294  // Jan 1, 1601. TTimeStamps use time in 100ns ticks since Jan 1, 1970.
295  // The difference is 134774 days.
296  fNanoSec = Int_t((time.QuadPart * (unsigned __int64) 100) %
297  (unsigned __int64) 1000000000);
298  time.QuadPart -=
299  (unsigned __int64) (1000*1000*10) // seconds
300  * (unsigned __int64) (60 * 60 * 24) // days
301  * (unsigned __int64) (134774); // # of days
302 
303  fSec = Int_t(time.QuadPart/(unsigned __int64) (1000*1000*10));
304 #else
305  // this should work on UNIX to get microsec precision
306  // we'll stick to a ns hack to make calls unique
307  struct timeval now;
308  if (!gettimeofday(&now,0)) {
309  fSec = now.tv_sec;
310  fNanoSec = now.tv_usec * 1000;
311  } else {
312  time_t nowtime;
313  time(&nowtime);
314  fSec = nowtime;
315  fNanoSec = 0;
316  }
317 #endif
318  static Int_t sec = 0, nsec = 0, fake_ns = 0;
319 
320  if (fSec == sec && fNanoSec == nsec) {
321  fNanoSec += ++fake_ns;
322  } else {
323  fake_ns = 0;
324  sec = fSec;
325  nsec = fNanoSec;
326  }
327 
328 }
329 
330 //_____________________________________________________________________________
331 void ValTimeStamp::Set(Int_t year, Int_t month, Int_t day,
332  Int_t hour, Int_t min, Int_t sec,
333  Int_t nsec, Bool_t isUTC, Int_t secOffset)
334 {
335  // Set Date/Time from components.
336  //
337  // month & day both use normal 1..12 and 1..31 counting
338  // hours, min, sec run from 0 to 23, 59, 59 respectively;
339  // secOffset provides method for adjusting for alternative timezones
340  //
341  // "year" | 0 1 ... 37 | 38...69 | 70 .. 100 101 .. 137
342  // true | 2000 2001 2037 | undefined | 1970 2000 2001 .. 2037
343  //
344  // "year" | 138...1969 | 1970 .. 2037 | ...
345  // true | undefined | 1970 .. 2037 | undefined
346  //
347 
348 
349  // deal with special formats of year
350  if (year <= 37) { year += 2000; }
351  if (year >= 70 && year <= 137) { year += 1900; }
352  // tm.tm_year is years since 1900
353  if (year >= 1900) { year -= 1900; }
354 
355  struct tm tmstruct;
356  tmstruct.tm_year = year; // years since 1900
357  tmstruct.tm_mon = month-1; // months since Jan [0,11]
358  tmstruct.tm_mday = day; // day of the month [1,31]
359  tmstruct.tm_hour = hour; // hours since midnight [0,23]
360  tmstruct.tm_min = min; // minutes after the hour [0,59]
361  tmstruct.tm_sec = sec + secOffset; // seconds after the minute [0,59]
362  tmstruct.tm_isdst = -1; // let "mktime" determine DST setting
363 
364  const time_t bad_time_t = (time_t) -1;
365  // convert tm struct to time_t, if values are given in UTC then
366  // no standard routine exists and we'll have to use our homegrown routine,
367  // if values are given in local time then use "mktime"
368  // which also normalizes the tm struct as a byproduct
369  time_t utc_sec = (isUTC) ? MktimeFromUTC(&tmstruct) : mktime(&tmstruct);
370 
371  // ValTimeStamp::Dump_tm_struct(tmstruct);
372 
373  if (utc_sec == bad_time_t)
374 
375  {
376  cout << "-I- ValTimeStamp::Set mktime returned -1" << endl;
377  }
378 
379  fSec = utc_sec;
380  fNanoSec = nsec;
381 
383 }
384 
385 //_____________________________________________________________________________
386 void ValTimeStamp::Set(Int_t date, Int_t time, Int_t nsec,
387  Bool_t isUTC, Int_t secOffset)
388 {
389  // Set date/time from integers of the form [yy]YYMMDD and HHMMSS,
390  // assume UTC (UTC) components:
391  //
392  // MM: 01=January .. 12=December
393  // DD: 01 .. 31
394  //
395  // HH: 00=midnight .. 23
396  // MM: 00 .. 59
397  // SS: 00 .. 69
398  //
399  // Date must be in format 980418 or 19980418
400  // 1001127 or 20001127 (i.e. year 100 = 2000).
401  // Time must be in format 224512 (second precision).
402  // Date must be >= 700101.
403 
404  Int_t year = date/10000;
405  Int_t month = (date-year*10000)/100;
406  Int_t day = date%100;
407 
408  // protect against odd attempts at time offsets
409  const Int_t oneday = 240000;
410  while (time < 0) {
411  time += oneday;
412  day -= 1;
413  }
414  while (time > oneday) {
415  time -= oneday;
416  day += 1;
417  }
418  Int_t hour = time/10000;
419  Int_t min = (time-hour*10000)/100;
420  Int_t sec = time%100;
421 
422  Set(year, month, day, hour, min, sec, nsec, isUTC, secOffset);
423 
424 }
425 
426 //_____________________________________________________________________________
428 {
429  // Ensure that the fNanoSec field is in range [0,99999999].
430 
431  // deal with negative values
432  while (fNanoSec < 0) {
433  fNanoSec += kNsPerSec;
434  fSec -= 1;
435  }
436  // deal with values inf fNanoSec greater than one sec
437  while (fNanoSec >= kNsPerSec) {
438  fNanoSec -= kNsPerSec;
439  fSec += 1;
440  }
441 }
442 //_____________________________________________________________________________
443 time_t ValTimeStamp::MktimeFromUTC(tm_t* tmstruct)
444 {
445  // Equivalent of standard routine "mktime" but
446  // using the assumption that tm struct is filled with UTC, not local, time.
447 
448  // This version *ISN'T* configured to handle every possible
449  // weirdness of out-of-range values in the case of normalizing
450  // the tm struct.
451 
452  // This version *DOESN'T* correctly handle values that can't be
453  // fit into a time_t (i.e. beyond year 2038-01-18 19:14:07, or
454  // before the start of Epoch).
455 
456  const Int_t days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
457  const Int_t daysLeap[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
458 
459  Int_t year = tmstruct->tm_year + 1900;
460  Bool_t isleap = ValTimeStamp::IsLeapYear(year);
461 
462  const Int_t* daysInMonth = days;
463  if (isleap) { daysInMonth = daysLeap; }
464 
465  // fill in tmstruct->tm_yday
466 
467  int& ref_tm_mon = tmstruct->tm_mon;
468  int& ref_tm_mday = tmstruct->tm_mday;
469  // count days in months past
470  tmstruct->tm_yday = 0;
471  for (Int_t imonth = 0; imonth < ref_tm_mon; imonth++) {
472  tmstruct->tm_yday += daysInMonth[imonth];
473  }
474  tmstruct->tm_yday += ref_tm_mday - 1; // day [1-31] but yday [0-365]
475 
476  // adjust if day in this month is more than the month has
477  while (ref_tm_mday > daysInMonth[ref_tm_mon]) {
478  ref_tm_mday -= daysInMonth[ref_tm_mon];
479  ref_tm_mon++;
480  }
481 
482  // *should* calculate tm_wday (0-6) here ...
483 
484  // UTC is never DST
485  tmstruct->tm_isdst = 0;
486 
487  // Calculate seconds since the Epoch based on formula in
488  // POSIX IEEEE Std 1003.1b-1993 pg 22
489 
490  Int_t utc_sec = tmstruct->tm_sec +
491  tmstruct->tm_min*60 +
492  tmstruct->tm_hour*3600 +
493  tmstruct->tm_yday*86400 +
494  (tmstruct->tm_year-70)*31536000 +
495  ((tmstruct->tm_year-69)/4)*86400;
496 
497  return utc_sec;
498 }
499 
500 //_____________________________________________________________________________
501 Bool_t ValTimeStamp::IsLeapYear(Int_t year)
502 {
503  // Is the given year a leap year.
504 
505 
506  // The calendar year is 365 days long, unless the year is exactly divisible
507  // by 4, in which case an extra day is added to February to make the year
508  // 366 days long. If the year is the last year of a century, eg. 1700, 1800,
509  // 1900, 2000, then it is only a leap year if it is exactly divisible by
510  // 400. Therefore, 1900 wasn't a leap year but 2000 was. The reason for
511  // these rules is to bring the average length of the calendar year into
512  // line with the length of the Earth's orbit around the Sun, so that the
513  // seasons always occur during the same months each year.
514 
515  if (year%4 != 0) {
516  return false;
517  } else {
518  if (year%400 == 0) {
519  return true;
520  } else {
521  if (year%100 == 0) {
522  return false;
523  } else {
524  return true;
525  }
526  }
527  }
528 
529 }
530 
531 //_____________________________________________________________________________
532 void ValTimeStamp::DumpTMStruct(const tm_t& tmstruct)
533 {
534  // Print out the "tm" structure:
535  // tmstruct.tm_year = year; // years since 1900
536  // tmstruct.tm_mon = month-1; // months since Jan [0,11]
537  // tmstruct.tm_mday = day; // day of the month [1,31]
538  // tmstruct.tm_hour = hour; // hours since midnight [0,23]
539  // tmstruct.tm_min = min; // minutes after the hour [0,59]
540  // tmstruct.tm_sec = sec; // seconds after the minute [0,59]
541  // tmstruct.tm_wday // day of week [0,6]
542  // tmstruct.tm_yday // days in year [0,365]
543  // tmstruct.tm_isdst // DST [-1/0/1] (unknown,false,true)
544 
545  printf(" tm { year %4d, mon %2d, day %2d,\n",
546  tmstruct.tm_year,
547  tmstruct.tm_mon,
548  tmstruct.tm_mday);
549  printf(" hour %2d, min %2d, sec %2d,\n",
550  tmstruct.tm_hour,
551  tmstruct.tm_min,
552  tmstruct.tm_sec);
553  printf(" wday %2d, yday %3d, isdst %2d",
554  tmstruct.tm_wday,
555  tmstruct.tm_yday,
556  tmstruct.tm_isdst);
557 #ifdef linux
558 //#ifdef __GNUC__
559 // special GCC extras
560  printf(",\n tm_gmtoff %7ld, tm_zone \"%s\"",
561 #ifdef __USE_BSD
562  tmstruct.tm_gmtoff,tmstruct.tm_zone);
563 #else
564  tmstruct.__tm_gmtoff,tmstruct.__tm_zone);
565 #endif
566 #endif
567  printf("}\n");
568 }
569 //_____________________________________________________________________________