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