]> git.proxmox.com Git - ceph.git/blob - ceph/src/include/utime.h
import quincy beta 17.1.0
[ceph.git] / ceph / src / include / utime.h
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 #ifndef CEPH_UTIME_H
16 #define CEPH_UTIME_H
17
18 #include <math.h>
19 #include <sys/time.h>
20 #include <time.h>
21 #include <errno.h>
22
23 #if defined(WITH_SEASTAR)
24 #include <seastar/core/lowres_clock.hh>
25 #endif
26
27 #include "include/compat.h"
28 #include "include/types.h"
29 #include "include/timegm.h"
30 #include "common/strtol.h"
31 #include "common/ceph_time.h"
32 #include "common/safe_io.h"
33 #include "common/SubProcess.h"
34 #include "include/denc.h"
35
36
37 // --------
38 // utime_t
39
40 inline __u32 cap_to_u32_max(__u64 t) {
41 return std::min(t, (__u64)std::numeric_limits<uint32_t>::max());
42 }
43 /* WARNING: If add member in utime_t, please make sure the encode/decode function
44 * work well. For little-endian machine, we should make sure there is no padding
45 * in 32-bit machine and 64-bit machine.
46 * You should also modify the padding_check function.
47 */
48 class utime_t {
49 public:
50 struct {
51 __u32 tv_sec, tv_nsec;
52 } tv;
53
54 public:
55 bool is_zero() const {
56 return (tv.tv_sec == 0) && (tv.tv_nsec == 0);
57 }
58
59 void normalize() {
60 if (tv.tv_nsec > 1000000000ul) {
61 tv.tv_sec = cap_to_u32_max(tv.tv_sec + tv.tv_nsec / (1000000000ul));
62 tv.tv_nsec %= 1000000000ul;
63 }
64 }
65
66 // cons
67 utime_t() { tv.tv_sec = 0; tv.tv_nsec = 0; }
68 utime_t(time_t s, int n) { tv.tv_sec = s; tv.tv_nsec = n; normalize(); }
69 utime_t(const struct ceph_timespec &v) {
70 decode_timeval(&v);
71 }
72 utime_t(const struct timespec v)
73 {
74 // NOTE: this is used by ceph_clock_now() so should be kept
75 // as thin as possible.
76 tv.tv_sec = v.tv_sec;
77 tv.tv_nsec = v.tv_nsec;
78 }
79 // conversion from ceph::real_time/coarse_real_time
80 template <typename Clock, typename std::enable_if_t<
81 ceph::converts_to_timespec_v<Clock>>* = nullptr>
82 explicit utime_t(const std::chrono::time_point<Clock>& t)
83 : utime_t(Clock::to_timespec(t)) {} // forward to timespec ctor
84
85 template<class Rep, class Period>
86 explicit utime_t(const std::chrono::duration<Rep, Period>& dur) {
87 using common_t = std::common_type_t<Rep, int>;
88 tv.tv_sec = std::max<common_t>(std::chrono::duration_cast<std::chrono::seconds>(dur).count(), 0);
89 tv.tv_nsec = std::max<common_t>((std::chrono::duration_cast<std::chrono::nanoseconds>(dur) %
90 std::chrono::seconds(1)).count(), 0);
91 }
92 #if defined(WITH_SEASTAR)
93 explicit utime_t(const seastar::lowres_system_clock::time_point& t) {
94 tv.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(
95 t.time_since_epoch()).count();
96 tv.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
97 t.time_since_epoch() % std::chrono::seconds(1)).count();
98 }
99 explicit operator seastar::lowres_system_clock::time_point() const noexcept {
100 using clock_t = seastar::lowres_system_clock;
101 return clock_t::time_point{std::chrono::duration_cast<clock_t::duration>(
102 std::chrono::seconds{tv.tv_sec} + std::chrono::nanoseconds{tv.tv_nsec})};
103 }
104 #endif
105
106 utime_t(const struct timeval &v) {
107 set_from_timeval(&v);
108 }
109 utime_t(const struct timeval *v) {
110 set_from_timeval(v);
111 }
112 void to_timespec(struct timespec *ts) const {
113 ts->tv_sec = tv.tv_sec;
114 ts->tv_nsec = tv.tv_nsec;
115 }
116 void set_from_double(double d) {
117 tv.tv_sec = (__u32)trunc(d);
118 tv.tv_nsec = (__u32)((d - (double)tv.tv_sec) * 1000000000.0);
119 }
120
121 ceph::real_time to_real_time() const {
122 ceph_timespec ts;
123 encode_timeval(&ts);
124 return ceph::real_clock::from_ceph_timespec(ts);
125 }
126
127 // accessors
128 time_t sec() const { return tv.tv_sec; }
129 long usec() const { return tv.tv_nsec/1000; }
130 int nsec() const { return tv.tv_nsec; }
131
132 // ref accessors/modifiers
133 __u32& sec_ref() { return tv.tv_sec; }
134 __u32& nsec_ref() { return tv.tv_nsec; }
135
136 uint64_t to_nsec() const {
137 return (uint64_t)tv.tv_nsec + (uint64_t)tv.tv_sec * 1000000000ull;
138 }
139 uint64_t to_msec() const {
140 return (uint64_t)tv.tv_nsec / 1000000ull + (uint64_t)tv.tv_sec * 1000ull;
141 }
142
143 void copy_to_timeval(struct timeval *v) const {
144 v->tv_sec = tv.tv_sec;
145 v->tv_usec = tv.tv_nsec/1000;
146 }
147 void set_from_timeval(const struct timeval *v) {
148 tv.tv_sec = v->tv_sec;
149 tv.tv_nsec = v->tv_usec*1000;
150 }
151 void padding_check() {
152 static_assert(
153 sizeof(utime_t) ==
154 sizeof(tv.tv_sec) +
155 sizeof(tv.tv_nsec)
156 ,
157 "utime_t have padding");
158 }
159 void encode(ceph::buffer::list &bl) const {
160 #if defined(CEPH_LITTLE_ENDIAN)
161 bl.append((char *)(this), sizeof(__u32) + sizeof(__u32));
162 #else
163 using ceph::encode;
164 encode(tv.tv_sec, bl);
165 encode(tv.tv_nsec, bl);
166 #endif
167 }
168 void decode(ceph::buffer::list::const_iterator &p) {
169 #if defined(CEPH_LITTLE_ENDIAN)
170 p.copy(sizeof(__u32) + sizeof(__u32), (char *)(this));
171 #else
172 using ceph::decode;
173 decode(tv.tv_sec, p);
174 decode(tv.tv_nsec, p);
175 #endif
176 }
177
178 DENC(utime_t, v, p) {
179 denc(v.tv.tv_sec, p);
180 denc(v.tv.tv_nsec, p);
181 }
182
183 void dump(ceph::Formatter *f) const;
184 static void generate_test_instances(std::list<utime_t*>& o);
185
186 void encode_timeval(struct ceph_timespec *t) const {
187 t->tv_sec = tv.tv_sec;
188 t->tv_nsec = tv.tv_nsec;
189 }
190 void decode_timeval(const struct ceph_timespec *t) {
191 tv.tv_sec = t->tv_sec;
192 tv.tv_nsec = t->tv_nsec;
193 }
194
195 utime_t round_to_minute() {
196 struct tm bdt;
197 time_t tt = sec();
198 localtime_r(&tt, &bdt);
199 bdt.tm_sec = 0;
200 tt = mktime(&bdt);
201 return utime_t(tt, 0);
202 }
203
204 utime_t round_to_hour() {
205 struct tm bdt;
206 time_t tt = sec();
207 localtime_r(&tt, &bdt);
208 bdt.tm_sec = 0;
209 bdt.tm_min = 0;
210 tt = mktime(&bdt);
211 return utime_t(tt, 0);
212 }
213
214 utime_t round_to_day() {
215 struct tm bdt;
216 time_t tt = sec();
217 localtime_r(&tt, &bdt);
218 bdt.tm_sec = 0;
219 bdt.tm_min = 0;
220 bdt.tm_hour = 0;
221 tt = mktime(&bdt);
222 return utime_t(tt, 0);
223 }
224
225 // cast to double
226 operator double() const {
227 return (double)sec() + ((double)nsec() / 1000000000.0f);
228 }
229 operator ceph_timespec() const {
230 ceph_timespec ts;
231 ts.tv_sec = sec();
232 ts.tv_nsec = nsec();
233 return ts;
234 }
235
236 void sleep() const {
237 struct timespec ts;
238 to_timespec(&ts);
239 nanosleep(&ts, NULL);
240 }
241
242 // output
243 std::ostream& gmtime(std::ostream& out, bool legacy_form=false) const {
244 out.setf(std::ios::right);
245 char oldfill = out.fill();
246 out.fill('0');
247 if (sec() < ((time_t)(60*60*24*365*10))) {
248 // raw seconds. this looks like a relative time.
249 out << (long)sec() << "." << std::setw(6) << usec();
250 } else {
251 // this looks like an absolute time.
252 // conform to http://en.wikipedia.org/wiki/ISO_8601
253 struct tm bdt;
254 time_t tt = sec();
255 gmtime_r(&tt, &bdt);
256 out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
257 << '-' << std::setw(2) << (bdt.tm_mon+1)
258 << '-' << std::setw(2) << bdt.tm_mday;
259 if (legacy_form) {
260 out << ' ';
261 } else {
262 out << 'T';
263 }
264 out << std::setw(2) << bdt.tm_hour
265 << ':' << std::setw(2) << bdt.tm_min
266 << ':' << std::setw(2) << bdt.tm_sec;
267 out << "." << std::setw(6) << usec();
268 out << "Z";
269 }
270 out.fill(oldfill);
271 out.unsetf(std::ios::right);
272 return out;
273 }
274
275 // output
276 std::ostream& gmtime_nsec(std::ostream& out) const {
277 out.setf(std::ios::right);
278 char oldfill = out.fill();
279 out.fill('0');
280 if (sec() < ((time_t)(60*60*24*365*10))) {
281 // raw seconds. this looks like a relative time.
282 out << (long)sec() << "." << std::setw(6) << usec();
283 } else {
284 // this looks like an absolute time.
285 // conform to http://en.wikipedia.org/wiki/ISO_8601
286 struct tm bdt;
287 time_t tt = sec();
288 gmtime_r(&tt, &bdt);
289 out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
290 << '-' << std::setw(2) << (bdt.tm_mon+1)
291 << '-' << std::setw(2) << bdt.tm_mday
292 << 'T'
293 << std::setw(2) << bdt.tm_hour
294 << ':' << std::setw(2) << bdt.tm_min
295 << ':' << std::setw(2) << bdt.tm_sec;
296 out << "." << std::setw(9) << nsec();
297 out << "Z";
298 }
299 out.fill(oldfill);
300 out.unsetf(std::ios::right);
301 return out;
302 }
303
304 // output
305 std::ostream& asctime(std::ostream& out) const {
306 out.setf(std::ios::right);
307 char oldfill = out.fill();
308 out.fill('0');
309 if (sec() < ((time_t)(60*60*24*365*10))) {
310 // raw seconds. this looks like a relative time.
311 out << (long)sec() << "." << std::setw(6) << usec();
312 } else {
313 // this looks like an absolute time.
314 struct tm bdt;
315 time_t tt = sec();
316 gmtime_r(&tt, &bdt);
317
318 char buf[128];
319 asctime_r(&bdt, buf);
320 int len = strlen(buf);
321 if (buf[len - 1] == '\n')
322 buf[len - 1] = '\0';
323 out << buf;
324 }
325 out.fill(oldfill);
326 out.unsetf(std::ios::right);
327 return out;
328 }
329
330 std::ostream& localtime(std::ostream& out, bool legacy_form=false) const {
331 out.setf(std::ios::right);
332 char oldfill = out.fill();
333 out.fill('0');
334 if (sec() < ((time_t)(60*60*24*365*10))) {
335 // raw seconds. this looks like a relative time.
336 out << (long)sec() << "." << std::setw(6) << usec();
337 } else {
338 // this looks like an absolute time.
339 // conform to http://en.wikipedia.org/wiki/ISO_8601
340 struct tm bdt;
341 time_t tt = sec();
342 localtime_r(&tt, &bdt);
343 out << std::setw(4) << (bdt.tm_year+1900) // 2007 -> '07'
344 << '-' << std::setw(2) << (bdt.tm_mon+1)
345 << '-' << std::setw(2) << bdt.tm_mday;
346 if (legacy_form) {
347 out << ' ';
348 } else {
349 out << 'T';
350 }
351 out << std::setw(2) << bdt.tm_hour
352 << ':' << std::setw(2) << bdt.tm_min
353 << ':' << std::setw(2) << bdt.tm_sec;
354 out << "." << std::setw(6) << usec();
355 if (!legacy_form) {
356 char buf[32] = { 0 };
357 strftime(buf, sizeof(buf), "%z", &bdt);
358 out << buf;
359 }
360 }
361 out.fill(oldfill);
362 out.unsetf(std::ios::right);
363 return out;
364 }
365
366 static int invoke_date(const std::string& date_str, utime_t *result) {
367 char buf[256];
368
369 SubProcess bin_date("/bin/date", SubProcess::CLOSE, SubProcess::PIPE,
370 SubProcess::KEEP);
371 bin_date.add_cmd_args("-d", date_str.c_str(), "+%s %N", NULL);
372
373 int r = bin_date.spawn();
374 if (r < 0) return r;
375
376 ssize_t n = safe_read(bin_date.get_stdout(), buf, sizeof(buf));
377
378 r = bin_date.join();
379 if (r || n <= 0) return -EINVAL;
380
381 uint64_t epoch, nsec;
382 std::istringstream iss(buf);
383
384 iss >> epoch;
385 iss >> nsec;
386
387 *result = utime_t(epoch, nsec);
388
389 return 0;
390 }
391
392
393 static int parse_date(const std::string& date, uint64_t *epoch, uint64_t *nsec,
394 std::string *out_date=nullptr,
395 std::string *out_time=nullptr) {
396 struct tm tm;
397 memset(&tm, 0, sizeof(tm));
398
399 if (nsec)
400 *nsec = 0;
401
402 const char *p = strptime(date.c_str(), "%Y-%m-%d", &tm);
403 if (p) {
404 if (*p == ' ' || *p == 'T') {
405 p++;
406 // strptime doesn't understand fractional/decimal seconds, and
407 // it also only takes format chars or literals, so we have to
408 // get creative.
409 char fmt[32] = {0};
410 strncpy(fmt, p, sizeof(fmt) - 1);
411 fmt[0] = '%';
412 fmt[1] = 'H';
413 fmt[2] = ':';
414 fmt[3] = '%';
415 fmt[4] = 'M';
416 fmt[6] = '%';
417 fmt[7] = 'S';
418 const char *subsec = 0;
419 char *q = fmt + 8;
420 if (*q == '.') {
421 ++q;
422 subsec = p + 9;
423 q = fmt + 9;
424 while (*q && isdigit(*q)) {
425 ++q;
426 }
427 }
428 // look for tz...
429 if (*q == '-' || *q == '+') {
430 *q = '%';
431 *(q+1) = 'z';
432 *(q+2) = 0;
433 }
434 p = strptime(p, fmt, &tm);
435 if (!p) {
436 return -EINVAL;
437 }
438 if (nsec && subsec) {
439 unsigned i;
440 char buf[10]; /* 9 digit + null termination */
441 for (i = 0; (i < sizeof(buf) - 1) && isdigit(*subsec); ++i, ++subsec) {
442 buf[i] = *subsec;
443 }
444 for (; i < sizeof(buf) - 1; ++i) {
445 buf[i] = '0';
446 }
447 buf[i] = '\0';
448 std::string err;
449 *nsec = (uint64_t)strict_strtol(buf, 10, &err);
450 if (!err.empty()) {
451 return -EINVAL;
452 }
453 }
454 }
455 } else {
456 int sec, usec;
457 int r = sscanf(date.c_str(), "%d.%d", &sec, &usec);
458 if (r != 2) {
459 return -EINVAL;
460 }
461
462 time_t tt = sec;
463 gmtime_r(&tt, &tm);
464
465 if (nsec) {
466 *nsec = (uint64_t)usec * 1000;
467 }
468 }
469
470 #ifndef _WIN32
471 // apply the tm_gmtoff manually below, since none of mktime,
472 // gmtime, and localtime seem to do it. zero it out here just in
473 // case some other libc *does* apply it. :(
474 auto gmtoff = tm.tm_gmtoff;
475 tm.tm_gmtoff = 0;
476 #else
477 auto gmtoff = _timezone;
478 #endif /* _WIN32 */
479
480 time_t t = internal_timegm(&tm);
481 if (epoch)
482 *epoch = (uint64_t)t;
483
484 *epoch -= gmtoff;
485
486 if (out_date) {
487 char buf[32];
488 strftime(buf, sizeof(buf), "%Y-%m-%d", &tm);
489 *out_date = buf;
490 }
491 if (out_time) {
492 char buf[32];
493 strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
494 *out_time = buf;
495 }
496
497 return 0;
498 }
499
500 bool parse(const std::string& s) {
501 uint64_t epoch, nsec;
502 int r = parse_date(s, &epoch, &nsec);
503 if (r < 0) {
504 return false;
505 }
506 *this = utime_t(epoch, nsec);
507 return true;
508 }
509 };
510 WRITE_CLASS_ENCODER(utime_t)
511 WRITE_CLASS_DENC(utime_t)
512
513 // arithmetic operators
514 inline utime_t operator+(const utime_t& l, const utime_t& r) {
515 __u64 sec = (__u64)l.sec() + r.sec();
516 return utime_t(cap_to_u32_max(sec), l.nsec() + r.nsec());
517 }
518 inline utime_t& operator+=(utime_t& l, const utime_t& r) {
519 l.sec_ref() = cap_to_u32_max((__u64)l.sec() + r.sec());
520 l.nsec_ref() += r.nsec();
521 l.normalize();
522 return l;
523 }
524 inline utime_t& operator+=(utime_t& l, double f) {
525 double fs = trunc(f);
526 double ns = (f - fs) * 1000000000.0;
527 l.sec_ref() = cap_to_u32_max(l.sec() + (__u64)fs);
528 l.nsec_ref() += (long)ns;
529 l.normalize();
530 return l;
531 }
532
533 inline utime_t operator-(const utime_t& l, const utime_t& r) {
534 return utime_t( l.sec() - r.sec() - (l.nsec()<r.nsec() ? 1:0),
535 l.nsec() - r.nsec() + (l.nsec()<r.nsec() ? 1000000000:0) );
536 }
537 inline utime_t& operator-=(utime_t& l, const utime_t& r) {
538 l.sec_ref() -= r.sec();
539 if (l.nsec() >= r.nsec())
540 l.nsec_ref() -= r.nsec();
541 else {
542 l.nsec_ref() += 1000000000L - r.nsec();
543 l.sec_ref()--;
544 }
545 return l;
546 }
547 inline utime_t& operator-=(utime_t& l, double f) {
548 double fs = trunc(f);
549 double ns = (f - fs) * 1000000000.0;
550 l.sec_ref() -= (long)fs;
551 long nsl = (long)ns;
552 if (nsl) {
553 l.sec_ref()--;
554 l.nsec_ref() = 1000000000L + l.nsec_ref() - nsl;
555 }
556 l.normalize();
557 return l;
558 }
559
560
561 // comparators
562 inline bool operator>(const utime_t& a, const utime_t& b)
563 {
564 return (a.sec() > b.sec()) || (a.sec() == b.sec() && a.nsec() > b.nsec());
565 }
566 inline bool operator<=(const utime_t& a, const utime_t& b)
567 {
568 return !(operator>(a, b));
569 }
570 inline bool operator<(const utime_t& a, const utime_t& b)
571 {
572 return (a.sec() < b.sec()) || (a.sec() == b.sec() && a.nsec() < b.nsec());
573 }
574 inline bool operator>=(const utime_t& a, const utime_t& b)
575 {
576 return !(operator<(a, b));
577 }
578
579 inline bool operator==(const utime_t& a, const utime_t& b)
580 {
581 return a.sec() == b.sec() && a.nsec() == b.nsec();
582 }
583 inline bool operator!=(const utime_t& a, const utime_t& b)
584 {
585 return a.sec() != b.sec() || a.nsec() != b.nsec();
586 }
587
588
589 // output
590
591 // ostream
592 inline std::ostream& operator<<(std::ostream& out, const utime_t& t)
593 {
594 return t.localtime(out);
595 }
596
597 inline std::string utimespan_str(const utime_t& age) {
598 auto age_ts = ceph::timespan(age.nsec()) + std::chrono::seconds(age.sec());
599 return ceph::timespan_str(age_ts);
600 }
601
602 #endif