Apache Mesos
duration.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_DURATION_HPP__
14 #define __STOUT_DURATION_HPP__
15 
16 #include <ctype.h> // For 'isdigit'.
17 
18 // For 'timeval'.
19 #ifndef __WINDOWS__
20 #include <sys/time.h>
21 #endif // __WINDOWS__
22 
23 #include <iomanip>
24 #include <iostream>
25 #include <limits>
26 #include <string>
27 
28 #include "error.hpp"
29 #include "numify.hpp"
30 #include "try.hpp"
31 
32 class Duration
33 {
34 public:
35  static Try<Duration> parse(const std::string& s);
36 
37  static Try<Duration> create(double seconds);
38 
39  constexpr Duration() : nanos(0) {}
40 
41  explicit Duration(const timeval& t)
42  {
43  nanos = t.tv_sec * SECONDS + t.tv_usec * MICROSECONDS;
44  }
45 
46  int64_t ns() const { return nanos; }
47  double us() const { return static_cast<double>(nanos) / MICROSECONDS; }
48  double ms() const { return static_cast<double>(nanos) / MILLISECONDS; }
49  double secs() const { return static_cast<double>(nanos) / SECONDS; }
50  double mins() const { return static_cast<double>(nanos) / MINUTES; }
51  double hrs() const { return static_cast<double>(nanos) / HOURS; }
52  double days() const { return static_cast<double>(nanos) / DAYS; }
53  double weeks() const { return static_cast<double>(nanos) / WEEKS; }
54 
55  struct timeval timeval() const
56  {
57  struct timeval t;
58 
59  // Explicitly compute `tv_sec` and `tv_usec` instead of using `us` and
60  // `secs` to avoid converting `int64_t` -> `double` -> `long`.
61  t.tv_sec = static_cast<decltype(t.tv_sec)>(ns() / SECONDS);
62  t.tv_usec = static_cast<decltype(t.tv_usec)>(
63  (ns() / MICROSECONDS) - (t.tv_sec * SECONDS / MICROSECONDS));
64  return t;
65  }
66 
67  bool operator<(const Duration& d) const { return nanos < d.nanos; }
68  bool operator<=(const Duration& d) const { return nanos <= d.nanos; }
69  bool operator>(const Duration& d) const { return nanos > d.nanos; }
70  bool operator>=(const Duration& d) const { return nanos >= d.nanos; }
71  bool operator==(const Duration& d) const { return nanos == d.nanos; }
72  bool operator!=(const Duration& d) const { return nanos != d.nanos; }
73 
75  {
76  nanos += that.nanos;
77  return *this;
78  }
79 
81  {
82  nanos -= that.nanos;
83  return *this;
84  }
85 
86  template <typename T>
87  Duration& operator*=(T multiplier)
88  {
89  nanos = static_cast<int64_t>(nanos * multiplier);
90  return *this;
91  }
92 
93  template <typename T>
94  Duration& operator/=(T divisor)
95  {
96  nanos = static_cast<int64_t>(nanos / divisor);
97  return *this;
98  }
99 
100  Duration operator+(const Duration& that) const
101  {
102  Duration sum = *this;
103  sum += that;
104  return sum;
105  }
106 
107  Duration operator-(const Duration& that) const
108  {
109  Duration diff = *this;
110  diff -= that;
111  return diff;
112  }
113 
114  template <typename T>
115  Duration operator*(T multiplier) const
116  {
117  Duration product = *this;
118  product *= multiplier;
119  return product;
120  }
121 
122  template <typename T>
123  Duration operator/(T divisor) const
124  {
125  Duration quotient = *this;
126  quotient /= divisor;
127  return quotient;
128  }
129 
130  // A constant holding the maximum value a Duration can have.
131  static constexpr Duration max();
132  // A constant holding the minimum (negative) value a Duration can
133  // have.
134  static constexpr Duration min();
135  // A constant holding a Duration of a "zero" value.
136  static constexpr Duration zero() { return Duration(); }
137 
138 protected:
139  static constexpr int64_t NANOSECONDS = 1;
140  static constexpr int64_t MICROSECONDS = 1000 * NANOSECONDS;
141  static constexpr int64_t MILLISECONDS = 1000 * MICROSECONDS;
142  static constexpr int64_t SECONDS = 1000 * MILLISECONDS;
143  static constexpr int64_t MINUTES = 60 * SECONDS;
144  static constexpr int64_t HOURS = 60 * MINUTES;
145  static constexpr int64_t DAYS = 24 * HOURS;
146  static constexpr int64_t WEEKS = 7 * DAYS;
147 
148  // Construct from a (value, unit) pair.
149  constexpr Duration(int64_t value, int64_t unit)
150  : nanos(value * unit) {}
151 
152 private:
153  // Used only by "parse".
154  constexpr Duration(double value, int64_t unit)
155  : nanos(static_cast<int64_t>(value * unit)) {}
156 
157  int64_t nanos;
158 
159  friend std::ostream& operator<<(
160  std::ostream& stream,
161  const Duration& duration);
162 };
163 
164 
165 class Nanoseconds : public Duration
166 {
167 public:
168  explicit constexpr Nanoseconds(int64_t nanoseconds)
169  : Duration(nanoseconds, NANOSECONDS) {}
170 
171  constexpr Nanoseconds(const Duration& d) : Duration(d) {}
172 
173  double value() const { return static_cast<double>(this->ns()); }
174 
175  static std::string units() { return "ns"; }
176 };
177 
178 
179 class Microseconds : public Duration
180 {
181 public:
182  explicit constexpr Microseconds(int64_t microseconds)
183  : Duration(microseconds, MICROSECONDS) {}
184 
185  constexpr Microseconds(const Duration& d) : Duration(d) {}
186 
187  double value() const { return this->us(); }
188 
189  static std::string units() { return "us"; }
190 };
191 
192 
193 class Milliseconds : public Duration
194 {
195 public:
196  explicit constexpr Milliseconds(int64_t milliseconds)
197  : Duration(milliseconds, MILLISECONDS) {}
198 
199  constexpr Milliseconds(const Duration& d) : Duration(d) {}
200 
201  double value() const { return this->ms(); }
202 
203  static std::string units() { return "ms"; }
204 };
205 
206 
207 class Seconds : public Duration
208 {
209 public:
210  explicit constexpr Seconds(int64_t seconds)
211  : Duration(seconds, SECONDS) {}
212 
213  constexpr Seconds(const Duration& d) : Duration(d) {}
214 
215  double value() const { return this->secs(); }
216 
217  static std::string units() { return "secs"; }
218 };
219 
220 
221 class Minutes : public Duration
222 {
223 public:
224  explicit constexpr Minutes(int64_t minutes)
225  : Duration(minutes, MINUTES) {}
226 
227  constexpr Minutes(const Duration& d) : Duration(d) {}
228 
229  double value() const { return this->mins(); }
230 
231  static std::string units() { return "mins"; }
232 };
233 
234 
235 class Hours : public Duration
236 {
237 public:
238  explicit constexpr Hours(int64_t hours)
239  : Duration(hours, HOURS) {}
240 
241  constexpr Hours(const Duration& d) : Duration(d) {}
242 
243  double value() const { return this->hrs(); }
244 
245  static std::string units() { return "hrs"; }
246 };
247 
248 
249 class Days : public Duration
250 {
251 public:
252  explicit constexpr Days(int64_t days)
253  : Duration(days, DAYS) {}
254 
255  constexpr Days(const Duration& d) : Duration(d) {}
256 
257  double value() const { return this->days(); }
258 
259  static std::string units() { return "days"; }
260 };
261 
262 
263 class Weeks : public Duration
264 {
265 public:
266  explicit constexpr Weeks(int64_t value) : Duration(value, WEEKS) {}
267 
268  constexpr Weeks(const Duration& d) : Duration(d) {}
269 
270  double value() const { return this->weeks(); }
271 
272  static std::string units() { return "weeks"; }
273 };
274 
275 
276 inline std::ostream& operator<<(std::ostream& stream, const Duration& duration_)
277 {
278  // Output the duration in full double precision and save the old precision.
279  std::streamsize precision =
280  stream.precision(std::numeric_limits<double>::digits10);
281 
282  // Parse the duration as the sign and the absolute value.
283  Duration duration = duration_;
284  if (duration_ < Duration::zero()) {
285  stream << "-";
286 
287  // Duration::min() may not be representable as a positive Duration.
288  if (duration_ == Duration::min()) {
289  duration = Duration::max();
290  } else {
291  duration = duration_ * -1;
292  }
293  }
294 
295  // First determine which bucket of time unit the duration falls into
296  // then check whether the duration can be represented as a whole
297  // number with this time unit or a smaller one.
298  // e.g. 1.42857142857143weeks falls into the 'Weeks' bucket but
299  // reads better with a smaller unit: '10days'. So we use 'days'
300  // instead of 'weeks' to output the duration.
301  int64_t nanoseconds = duration.ns();
302  if (duration < Microseconds(1)) {
303  stream << duration.ns() << Nanoseconds::units();
304  } else if (duration < Milliseconds(1)) {
305  if (nanoseconds % Duration::MICROSECONDS != 0) {
306  // We can't get a whole number using this unit but we can at
307  // one level down.
308  stream << duration.ns() << Nanoseconds::units();
309  } else {
310  stream << duration.us() << Microseconds::units();
311  }
312  } else if (duration < Seconds(1)) {
313  if (nanoseconds % Duration::MILLISECONDS != 0 &&
314  nanoseconds % Duration::MICROSECONDS == 0) {
315  stream << duration.us() << Microseconds::units();
316  } else {
317  stream << duration.ms() << Milliseconds::units();
318  }
319  } else if (duration < Minutes(1)) {
320  if (nanoseconds % Duration::SECONDS != 0 &&
321  nanoseconds % Duration::MILLISECONDS == 0) {
322  stream << duration.ms() << Milliseconds::units();
323  } else {
324  stream << duration.secs() << Seconds::units();
325  }
326  } else if (duration < Hours(1)) {
327  if (nanoseconds % Duration::MINUTES != 0 &&
328  nanoseconds % Duration::SECONDS == 0) {
329  stream << duration.secs() << Seconds::units();
330  } else {
331  stream << duration.mins() << Minutes::units();
332  }
333  } else if (duration < Days(1)) {
334  if (nanoseconds % Duration::HOURS != 0 &&
335  nanoseconds % Duration::MINUTES == 0) {
336  stream << duration.mins() << Minutes::units();
337  } else {
338  stream << duration.hrs() << Hours::units();
339  }
340  } else if (duration < Weeks(1)) {
341  if (nanoseconds % Duration::DAYS != 0 &&
342  nanoseconds % Duration::HOURS == 0) {
343  stream << duration.hrs() << Hours::units();
344  } else {
345  stream << duration.days() << Days::units();
346  }
347  } else {
348  if (nanoseconds % Duration::WEEKS != 0 &&
349  nanoseconds % Duration::DAYS == 0) {
350  stream << duration.days() << Days::units();
351  } else {
352  stream << duration.weeks() << Weeks::units();
353  }
354  }
355 
356  // Return the stream to original formatting state.
357  stream.precision(precision);
358 
359  return stream;
360 }
361 
362 
363 inline Try<Duration> Duration::parse(const std::string& s)
364 {
365  // TODO(benh): Support negative durations (i.e., starts with '-').
366  size_t index = 0;
367  while (index < s.size()) {
368  if (isdigit(s[index]) || s[index] == '.') {
369  index++;
370  continue;
371  }
372 
373  Try<double> value = numify<double>(s.substr(0, index));
374 
375  if (value.isError()) {
376  return Error(value.error());
377  }
378 
379  const std::string unit = s.substr(index);
380 
381  int64_t factor;
382  if (unit == "ns") {
383  factor = NANOSECONDS;
384  } else if (unit == "us") {
385  factor = MICROSECONDS;
386  } else if (unit == "ms") {
387  factor = MILLISECONDS;
388  } else if (unit == "secs") {
389  factor = SECONDS;
390  } else if (unit == "mins") {
391  factor = MINUTES;
392  } else if (unit == "hrs") {
393  factor = HOURS;
394  } else if (unit == "days") {
395  factor = DAYS;
396  } else if (unit == "weeks") {
397  factor = WEEKS;
398  } else {
399  return Error(
400  "Unknown duration unit '" + unit + "'; supported units are"
401  " 'ns', 'us', 'ms', 'secs', 'mins', 'hrs', 'days', and 'weeks'");
402  }
403 
404  double nanos = value.get() * factor;
405  if (nanos > max().nanos || nanos < min().nanos) {
406  return Error(
407  "Argument out of the range that a Duration can represent due"
408  " to int64_t's size limit");
409  }
410 
411  return Duration(value.get(), factor);
412  }
413 
414  return Error("Invalid duration '" + s + "'");
415 }
416 
417 
418 inline Try<Duration> Duration::create(double seconds)
419 {
420  if (seconds * SECONDS > max().nanos || seconds * SECONDS < min().nanos) {
421  return Error("Argument out of the range that a Duration can represent due "
422  "to int64_t's size limit");
423  }
424 
425  return Nanoseconds(static_cast<int64_t>(seconds * SECONDS));
426 }
427 
428 
429 inline constexpr Duration Duration::max()
430 {
432 }
433 
434 
435 inline constexpr Duration Duration::min()
436 {
438 }
439 
440 #endif // __STOUT_DURATION_HPP__
double value() const
Definition: duration.hpp:257
double weeks() const
Definition: duration.hpp:53
Duration operator/(T divisor) const
Definition: duration.hpp:123
Try< Diff > diff(const std::string &from, const std::string &to)
Definition: svn.hpp:72
static std::string units()
Definition: duration.hpp:189
double value() const
Definition: duration.hpp:201
Definition: errorbase.hpp:36
static constexpr int64_t MICROSECONDS
Definition: duration.hpp:140
double value() const
Definition: duration.hpp:173
Duration & operator-=(const Duration &that)
Definition: duration.hpp:80
bool operator!=(const Duration &d) const
Definition: duration.hpp:72
double value() const
Definition: duration.hpp:243
T & get()&
Definition: try.hpp:73
Definition: check.hpp:33
bool operator>=(const Duration &d) const
Definition: duration.hpp:70
static std::string units()
Definition: duration.hpp:231
Duration operator*(T multiplier) const
Definition: duration.hpp:115
Duration operator+(const Duration &that) const
Definition: duration.hpp:100
double value() const
Definition: duration.hpp:229
constexpr Hours(const Duration &d)
Definition: duration.hpp:241
bool operator>(const Duration &d) const
Definition: duration.hpp:69
constexpr Duration(int64_t value, int64_t unit)
Definition: duration.hpp:149
static constexpr int64_t HOURS
Definition: duration.hpp:144
static constexpr int64_t MINUTES
Definition: duration.hpp:143
static constexpr int64_t SECONDS
Definition: duration.hpp:142
constexpr Microseconds(const Duration &d)
Definition: duration.hpp:185
constexpr Days(int64_t days)
Definition: duration.hpp:252
static Try< Duration > create(double seconds)
Definition: duration.hpp:418
double ms() const
Definition: duration.hpp:48
constexpr Weeks(int64_t value)
Definition: duration.hpp:266
bool operator==(const Duration &d) const
Definition: duration.hpp:71
Definition: duration.hpp:32
Duration(const timeval &t)
Definition: duration.hpp:41
constexpr Milliseconds(const Duration &d)
Definition: duration.hpp:199
double mins() const
Definition: duration.hpp:50
Duration operator-(const Duration &that) const
Definition: duration.hpp:107
static constexpr int64_t DAYS
Definition: duration.hpp:145
static std::string units()
Definition: duration.hpp:245
double days() const
Definition: duration.hpp:52
bool operator<=(const Duration &d) const
Definition: duration.hpp:68
static std::string units()
Definition: duration.hpp:259
Definition: duration.hpp:193
Duration & operator*=(T multiplier)
Definition: duration.hpp:87
constexpr Minutes(int64_t minutes)
Definition: duration.hpp:224
Definition: duration.hpp:221
Option< T > max(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:208
Definition: duration.hpp:207
static constexpr int64_t MILLISECONDS
Definition: duration.hpp:141
constexpr Days(const Duration &d)
Definition: duration.hpp:255
constexpr Minutes(const Duration &d)
Definition: duration.hpp:227
int64_t ns() const
Definition: duration.hpp:46
Option< T > min(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:179
constexpr Weeks(const Duration &d)
Definition: duration.hpp:268
double value() const
Definition: duration.hpp:215
Duration & operator+=(const Duration &that)
Definition: duration.hpp:74
Definition: duration.hpp:249
double secs() const
Definition: duration.hpp:49
bool operator<(const Duration &d) const
Definition: duration.hpp:67
static Try error(const E &e)
Definition: try.hpp:42
static constexpr int64_t NANOSECONDS
Definition: duration.hpp:139
static Try< Duration > parse(const std::string &s)
Definition: duration.hpp:363
Definition: duration.hpp:235
Duration & operator/=(T divisor)
Definition: duration.hpp:94
double hrs() const
Definition: duration.hpp:51
constexpr Seconds(const Duration &d)
Definition: duration.hpp:213
Definition: duration.hpp:179
bool isError() const
Definition: try.hpp:71
static std::string units()
Definition: duration.hpp:217
static constexpr Duration zero()
Definition: duration.hpp:136
double value() const
Definition: duration.hpp:187
constexpr Hours(int64_t hours)
Definition: duration.hpp:238
double us() const
Definition: duration.hpp:47
struct timeval timeval() const
Definition: duration.hpp:55
friend std::ostream & operator<<(std::ostream &stream, const Duration &duration)
Definition: duration.hpp:276
constexpr Nanoseconds(const Duration &d)
Definition: duration.hpp:171
static std::string units()
Definition: duration.hpp:272
Definition: duration.hpp:165
double value() const
Definition: duration.hpp:270
static constexpr int64_t WEEKS
Definition: duration.hpp:146
constexpr Duration()
Definition: duration.hpp:39
constexpr Milliseconds(int64_t milliseconds)
Definition: duration.hpp:196
Definition: duration.hpp:263
static constexpr Duration min()
Definition: duration.hpp:435
static std::string units()
Definition: duration.hpp:175
static constexpr Duration max()
Definition: duration.hpp:429
static std::string units()
Definition: duration.hpp:203
constexpr Seconds(int64_t seconds)
Definition: duration.hpp:210
constexpr Microseconds(int64_t microseconds)
Definition: duration.hpp:182
constexpr Nanoseconds(int64_t nanoseconds)
Definition: duration.hpp:168