]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/ceph_time.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / common / ceph_time.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 // For ceph_timespec
16 #include "ceph_time.h"
17 #include "log/LogClock.h"
18 #include "config.h"
19 #include "strtol.h"
20
21 #if defined(__APPLE__)
22 #include <mach/mach.h>
23 #include <mach/mach_time.h>
24
25 #include <ostringstream>
26
27 #ifndef NSEC_PER_SEC
28 #define NSEC_PER_SEC 1000000000ULL
29 #endif
30
31 int clock_gettime(int clk_id, struct timespec *tp)
32 {
33 if (clk_id == CLOCK_REALTIME) {
34 // gettimeofday is much faster than clock_get_time
35 struct timeval now;
36 int ret = gettimeofday(&now, NULL);
37 if (ret)
38 return ret;
39 tp->tv_sec = now.tv_sec;
40 tp->tv_nsec = now.tv_usec * 1000L;
41 } else {
42 uint64_t t = mach_absolute_time();
43 static mach_timebase_info_data_t timebase_info;
44 if (timebase_info.denom == 0) {
45 (void)mach_timebase_info(&timebase_info);
46 }
47 auto nanos = t * timebase_info.numer / timebase_info.denom;
48 tp->tv_sec = nanos / NSEC_PER_SEC;
49 tp->tv_nsec = nanos - (tp->tv_sec * NSEC_PER_SEC);
50 }
51 return 0;
52 }
53 #endif
54
55 namespace ceph {
56 namespace time_detail {
57 void real_clock::to_ceph_timespec(const time_point& t,
58 struct ceph_timespec& ts) {
59 ts.tv_sec = to_time_t(t);
60 ts.tv_nsec = (t.time_since_epoch() % seconds(1)).count();
61 }
62 struct ceph_timespec real_clock::to_ceph_timespec(const time_point& t) {
63 struct ceph_timespec ts;
64 to_ceph_timespec(t, ts);
65 return ts;
66 }
67 real_clock::time_point real_clock::from_ceph_timespec(
68 const struct ceph_timespec& ts) {
69 return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec));
70 }
71
72 void coarse_real_clock::to_ceph_timespec(const time_point& t,
73 struct ceph_timespec& ts) {
74 ts.tv_sec = to_time_t(t);
75 ts.tv_nsec = (t.time_since_epoch() % seconds(1)).count();
76 }
77 struct ceph_timespec coarse_real_clock::to_ceph_timespec(
78 const time_point& t) {
79 struct ceph_timespec ts;
80 to_ceph_timespec(t, ts);
81 return ts;
82 }
83 coarse_real_clock::time_point coarse_real_clock::from_ceph_timespec(
84 const struct ceph_timespec& ts) {
85 return time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec));
86 }
87
88 }
89
90 using std::chrono::duration_cast;
91 using std::chrono::seconds;
92 using std::chrono::microseconds;
93
94 template<typename Clock,
95 typename std::enable_if<Clock::is_steady>::type*>
96 std::ostream& operator<<(std::ostream& m,
97 const std::chrono::time_point<Clock>& t) {
98 return m << std::fixed << std::chrono::duration<double>(
99 t.time_since_epoch()).count()
100 << 's';
101 }
102
103 std::ostream& operator<<(std::ostream& m, const timespan& t) {
104 static_assert(std::is_unsigned_v<timespan::rep>);
105 m << std::chrono::duration_cast<std::chrono::seconds>(t).count();
106 if (auto ns = (t % 1s).count(); ns > 0) {
107 char oldfill = m.fill();
108 m.fill('0');
109 m << '.' << std::setw(9) << ns;
110 m.fill(oldfill);
111 }
112 return m << 's';
113 }
114
115 template<typename Clock,
116 typename std::enable_if<!Clock::is_steady>::type*>
117 std::ostream& operator<<(std::ostream& m,
118 const std::chrono::time_point<Clock>& t) {
119 m.setf(std::ios::right);
120 char oldfill = m.fill();
121 m.fill('0');
122 // localtime. this looks like an absolute time.
123 // conform to http://en.wikipedia.org/wiki/ISO_8601
124 struct tm bdt;
125 time_t tt = Clock::to_time_t(t);
126 localtime_r(&tt, &bdt);
127 char tz[32] = { 0 };
128 strftime(tz, sizeof(tz), "%z", &bdt);
129 m << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
130 << '-' << std::setw(2) << (bdt.tm_mon+1)
131 << '-' << std::setw(2) << bdt.tm_mday
132 << 'T'
133 << std::setw(2) << bdt.tm_hour
134 << ':' << std::setw(2) << bdt.tm_min
135 << ':' << std::setw(2) << bdt.tm_sec
136 << "." << std::setw(6) << duration_cast<microseconds>(
137 t.time_since_epoch() % seconds(1)).count()
138 << tz;
139 m.fill(oldfill);
140 m.unsetf(std::ios::right);
141 return m;
142 }
143
144 template std::ostream&
145 operator<< <mono_clock>(std::ostream& m, const mono_time& t);
146 template std::ostream&
147 operator<< <real_clock>(std::ostream& m, const real_time& t);
148 template std::ostream&
149 operator<< <coarse_mono_clock>(std::ostream& m, const coarse_mono_time& t);
150 template std::ostream&
151 operator<< <coarse_real_clock>(std::ostream& m, const coarse_real_time& t);
152
153 std::string timespan_str(timespan t)
154 {
155 // FIXME: somebody pretty please make a version of this function
156 // that isn't as lame as this one!
157 uint64_t nsec = std::chrono::nanoseconds(t).count();
158 ostringstream ss;
159 if (nsec < 2000000000) {
160 ss << ((float)nsec / 1000000000) << "s";
161 return ss.str();
162 }
163 uint64_t sec = nsec / 1000000000;
164 if (sec < 120) {
165 ss << sec << "s";
166 return ss.str();
167 }
168 uint64_t min = sec / 60;
169 if (min < 120) {
170 ss << min << "m";
171 return ss.str();
172 }
173 uint64_t hr = min / 60;
174 if (hr < 48) {
175 ss << hr << "h";
176 return ss.str();
177 }
178 uint64_t day = hr / 24;
179 if (day < 14) {
180 ss << day << "d";
181 return ss.str();
182 }
183 uint64_t wk = day / 7;
184 if (wk < 12) {
185 ss << wk << "w";
186 return ss.str();
187 }
188 uint64_t mn = day / 30;
189 if (mn < 24) {
190 ss << mn << "M";
191 return ss.str();
192 }
193 uint64_t yr = day / 365;
194 ss << yr << "y";
195 return ss.str();
196 }
197
198 std::string exact_timespan_str(timespan t)
199 {
200 uint64_t nsec = std::chrono::nanoseconds(t).count();
201 uint64_t sec = nsec / 1000000000;
202 nsec %= 1000000000;
203 uint64_t yr = sec / (60 * 60 * 24 * 365);
204 ostringstream ss;
205 if (yr) {
206 ss << yr << "y";
207 sec -= yr * (60 * 60 * 24 * 365);
208 }
209 uint64_t mn = sec / (60 * 60 * 24 * 30);
210 if (mn >= 3) {
211 ss << mn << "mo";
212 sec -= mn * (60 * 60 * 24 * 30);
213 }
214 uint64_t wk = sec / (60 * 60 * 24 * 7);
215 if (wk >= 2) {
216 ss << wk << "w";
217 sec -= wk * (60 * 60 * 24 * 7);
218 }
219 uint64_t day = sec / (60 * 60 * 24);
220 if (day >= 2) {
221 ss << day << "d";
222 sec -= day * (60 * 60 * 24);
223 }
224 uint64_t hr = sec / (60 * 60);
225 if (hr >= 2) {
226 ss << hr << "h";
227 sec -= hr * (60 * 60);
228 }
229 uint64_t min = sec / 60;
230 if (min >= 2) {
231 ss << min << "m";
232 sec -= min * 60;
233 }
234 if (sec) {
235 ss << sec;
236 }
237 if (nsec) {
238 ss << ((float)nsec / 1000000000);
239 }
240 if (sec || nsec) {
241 ss << "s";
242 }
243 return ss.str();
244 }
245
246 std::chrono::seconds parse_timespan(const std::string& s)
247 {
248 static std::map<string,int> units = {
249 { "s", 1 },
250 { "sec", 1 },
251 { "second", 1 },
252 { "seconds", 1 },
253 { "m", 60 },
254 { "min", 60 },
255 { "minute", 60 },
256 { "minutes", 60 },
257 { "h", 60*60 },
258 { "hr", 60*60 },
259 { "hour", 60*60 },
260 { "hours", 60*60 },
261 { "d", 24*60*60 },
262 { "day", 24*60*60 },
263 { "days", 24*60*60 },
264 { "w", 7*24*60*60 },
265 { "wk", 7*24*60*60 },
266 { "week", 7*24*60*60 },
267 { "weeks", 7*24*60*60 },
268 { "mo", 30*24*60*60 },
269 { "month", 30*24*60*60 },
270 { "months", 30*24*60*60 },
271 { "y", 365*24*60*60 },
272 { "yr", 365*24*60*60 },
273 { "year", 365*24*60*60 },
274 { "years", 365*24*60*60 },
275 };
276
277 auto r = 0s;
278 auto pos = 0u;
279 while (pos < s.size()) {
280 // skip whitespace
281 while (std::isspace(s[pos])) {
282 ++pos;
283 }
284 if (pos >= s.size()) {
285 break;
286 }
287
288 // consume any digits
289 auto val_start = pos;
290 while (std::isdigit(s[pos])) {
291 ++pos;
292 }
293 if (val_start == pos) {
294 throw invalid_argument("expected digit");
295 }
296 string n = s.substr(val_start, pos - val_start);
297 string err;
298 auto val = strict_strtoll(n.c_str(), 10, &err);
299 if (err.size()) {
300 throw invalid_argument(err);
301 }
302
303 // skip whitespace
304 while (std::isspace(s[pos])) {
305 ++pos;
306 }
307
308 // consume unit
309 auto unit_start = pos;
310 while (std::isalpha(s[pos])) {
311 ++pos;
312 }
313 if (unit_start != pos) {
314 string unit = s.substr(unit_start, pos - unit_start);
315 auto p = units.find(unit);
316 if (p == units.end()) {
317 throw invalid_argument("unrecogized unit '"s + unit + "'");
318 }
319 val *= p->second;
320 } else if (pos < s.size()) {
321 throw invalid_argument("unexpected trailing '"s + s.substr(pos) + "'");
322 }
323 r += chrono::seconds(val);
324 }
325 return r;
326 }
327
328 }