1 
2 /* Web Polygraph       http://www.web-polygraph.org/
3  * Copyright 2003-2011 The Measurement Factory
4  * Licensed under the Apache License, Version 2.0 */
5 
6 #include "xstd/xstd.h"
7 
8 #include <limits.h>
9 #include <stdlib.h>
10 #include "xstd/h/iostream.h"
11 #include "xstd/h/iomanip.h"
12 
13 #include "xstd/Assert.h"
14 #include "xstd/Time.h"
15 #include "xstd/xport.h"
16 #include "xstd/gadgets.h"
17 
18 
19 #if !defined(HAVE_GETTIMEOFDAY) && defined(HAVE__FTIME)
20 #include <sys/timeb.h>
21 static
gettimeofday(Time * tm,void *)22 void gettimeofday(Time *tm, void *) {
23 	struct _timeb tb;
24 	_ftime(&tb);
25 	tm->tv_sec  = tb.time;
26 	tm->tv_usec = tb.millitm;
27 }
28 #endif
29 
30 
Now()31 Time Time::Now() {
32 	Time tm;
33 	gettimeofday(&tm, 0);
34 
35 	// Some Linux kernels have gettimeofday bugs that cause time jumps
36 	// of approximately 72 minutes, especially on SMP systems. We saw it
37 	// on an 8-CPU box running 2.6.18-8.el5. For 2002 discussion, start at
38 	// http://www.kernel-traffic.org/kernel-traffic/kt20020708_174.html#1
39 	// Here, we try to ignore individual jumps exceeding 60 minutes.
40 	static Time ptm = tm; // previous time
41 	if (tm.tv_sec - ptm.tv_sec > 3600L) {
42 		clog << "Warning: ignoring " << (tm - ptm).secd() <<
43 			"sec time jump" << endl <<
44 			"\tfrom: " << ptm.tv_sec <<'.'<< ptm.tv_usec << endl <<
45 			"\tto:   " << tm.tv_sec << '.' << tm.tv_usec << endl;
46 		const Time result = ptm;
47 		ptm = tm; // if the strange value persists, honor it
48 		return result;
49 	}
50 	ptm = tm;
51 
52 	return tm;
53 }
54 
Max()55 Time Time::Max() {
56 	return Time(INT_MAX, INT_MAX);
57 }
58 
Msec(long msec)59 Time Time::Msec(long msec) {
60 	return Time(msec / 1000, 1000*(msec % 1000));
61 }
62 
Secd(double dsec)63 Time Time::Secd(double dsec) {
64 	if (!Should(dsec <= LONG_MAX))
65 		dsec = LONG_MAX;
66 	else
67 	if (!Should(dsec >= LONG_MIN))
68 		dsec = LONG_MIN;
69 	const long sec = (long)dsec;
70 	return Time(sec, (long)((dsec-sec)*1e6));
71 }
72 
Time(const struct tm & t)73 Time::Time(const struct tm &t) {
74 	struct tm t2 = t;    // must copy -- timegm modifies its param
75 	if (t2.tm_year < 70) // a Y2K plug-in!
76 		t2.tm_year += 100;
77 	tv_sec = (0 <= t2.tm_mon && t2.tm_mon < 12) ? xtimegm(&t2) : -1;
78 	tv_usec = tv_sec >= 0 ? 0 : -1;
79 }
80 
operator +=(const Time & tm)81 Time &Time::operator +=(const Time &tm) {
82 
83 	tv_sec += tm.tv_sec;
84 	tv_usec += tm.tv_usec;
85 
86 	if (tv_usec >= 1000000L) {
87 		tv_usec -= 1000000L;
88 		tv_sec++;
89 	}
90 
91 	return *this;
92 }
93 
94 // note: negative times are confusing for humans; e.g., "-1:1" means "-0.1"
operator -=(const Time & tm)95 Time &Time::operator -=(const Time &tm) {
96 
97 	tv_sec -= tm.tv_sec;
98 	tv_usec -= tm.tv_usec;
99 
100 	if (tv_usec < 0) {
101 		tv_usec += 1000000L;
102 		tv_sec--;
103 	}
104 
105 	return *this;
106 }
107 
operator *=(int factor)108 Time &Time::operator *=(int factor) {
109 	tv_sec *= factor;
110 	tv_usec *= factor;
111 
112 	if (tv_usec >= 1000000L) {
113 		tv_sec += tv_usec/1000000L;
114 		tv_usec %= 1000000L;
115 	} else
116 	if (tv_usec < 0) {
117 		tv_usec = -tv_usec;
118 		tv_sec -= tv_usec/1000000L;
119 		tv_sec--;
120 		tv_usec = 1000000L - (tv_usec % 1000000L);
121 	}
122 
123 	return *this;
124 }
125 
operator /=(double factor)126 Time &Time::operator /=(double factor) {
127 	if (factor) {
128 		const double dsec = secd()/factor;
129 		tv_sec = (long) dsec;
130 		tv_usec = (long)((dsec-tv_sec)*1e6);
131 	} else {
132 		tv_sec = tv_usec = -1;
133 	}
134 
135 	return *this;
136 }
137 
gmtime() const138 struct tm *Time::gmtime() const {
139 	time_t clock = (time_t) tv_sec;
140 	return ::gmtime(&clock);
141 }
142 
read(istream & is)143 istream &Time::read(istream &is) {
144 	char c;
145 	return is >> tv_sec >> c >> tv_usec;
146 }
147 
print(ostream & os) const148 ostream &Time::print(ostream &os) const {
149 	if (tv_sec < 60*60*24*365 && tv_usec >= 0)
150 		return printInterval(os);
151 	if (tv_sec == -1 && tv_usec == -1)
152 		return os << "<none>"; // works for everybody?
153 
154 	const char osfill = os.fill();
155 	return os << tv_sec << '.' << setw(6) << setfill('0') << tv_usec << setfill(osfill);
156 }
157 
printInterval(ostream & os) const158 ostream &Time::printInterval(ostream &os) const {
159 
160 	const int osprec = os.precision(2);
161 	const char osfill = os.fill();
162 
163 	if (tv_sec < 0)
164 		os << tv_sec << '.' << setfill('0') << setw(6) << tv_usec << "sec";
165 	else
166 	if (tv_sec == 0)
167 		if (tv_usec < 1000)
168 			os << tv_usec << "usec";
169 		else
170 			os << (tv_usec/1000) << "msec";
171 	else
172 	if (tv_sec < 60)
173 		os << secd() << "sec";
174 	else
175 	if (tv_sec < 60*60)
176 		os << secd()/60 << "min";
177 	else
178 	if (tv_sec < 24*60*60)
179 		os << secd()/(60*60) << "hour";
180 	else
181 	if (tv_sec < 365*24*60*60)
182 		os << secd()/(24*60*60) << "day";
183 	else
184 		os << secd()/(365*24*60*60) << "year";
185 
186 	os.fill(osfill);
187 	os.precision(osprec);
188 
189 	return os;
190 }
191 
192 // useful for debugging
operator <<(ostream & os,const struct tm & t)193 ostream &operator <<(ostream &os, const struct tm &t) {
194 	return os << here << endl
195 		<< '\t' << "tm_mday: " << "\t " << setw(4) << t.tm_mday << endl
196 		<< '\t' << "tm_mon:  " << "\t " << setw(4) << t.tm_mon << endl
197 		<< '\t' << "tm_year: " << "\t " << setw(4) << t.tm_year << endl
198 		<< '\t' << "tm_hour: " << "\t " << setw(4) << t.tm_hour << endl
199 		<< '\t' << "tm_min:  " << "\t " << setw(4) << t.tm_min << endl
200 		<< '\t' << "tm_sec:  " << "\t " << setw(4) << t.tm_sec << endl
201 		<< '\t' << "tv_sec:  " << "\t " << setw(4) << Time(t) << endl
202 		<< endl;
203 }
204