Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Time.hpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2022 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#ifndef TOOLBOX_SYS_TIME_HPP
18#define TOOLBOX_SYS_TIME_HPP
19
21
22#include <boost/io/ios_state.hpp>
23
24#include <chrono>
25#include <iomanip>
26#include <optional>
27
28namespace toolbox {
29inline namespace sys {
30using namespace std::literals::chrono_literals;
31using namespace std::literals::string_view_literals;
32
33using Seconds = std::chrono::seconds;
34using Decis = std::chrono::duration<int64_t, std::deci>;
35using Millis = std::chrono::milliseconds;
36using Micros = std::chrono::microseconds;
37using Nanos = std::chrono::nanoseconds;
38
41
43
44struct MonoClock {
46 using period = Duration::period;
47 using rep = Duration::rep;
48 using time_point = std::chrono::time_point<MonoClock, Duration>;
49
50 static constexpr int Id{CLOCK_MONOTONIC};
51 static constexpr bool is_steady{true};
52
53 static constexpr time_point max() noexcept
54 {
55 using namespace std::chrono;
56 using FromPoint = std::chrono::time_point<MonoClock, seconds>;
57 constexpr seconds secs{std::numeric_limits<int>::max()};
59 }
60
62
63 static constexpr std::time_t to_time_t(const time_point& tp) noexcept
64 {
65 using namespace std::chrono;
66 return duration_cast<seconds>(tp.time_since_epoch()).count();
67 }
68
69 static constexpr time_point from_time_t(std::time_t t) noexcept
70 {
71 using namespace std::chrono;
72 using FromPoint = std::chrono::time_point<MonoClock, seconds>;
74 }
75};
76
77struct WallClock {
79 using period = Duration::period;
80 using rep = Duration::rep;
81 using time_point = std::chrono::time_point<WallClock, Duration>;
82
83 static constexpr int Id{CLOCK_REALTIME};
84 static constexpr bool is_steady{false};
85
86 static constexpr time_point max() noexcept
87 {
88 using namespace std::chrono;
89 using FromPoint = std::chrono::time_point<WallClock, seconds>;
90 constexpr seconds secs{std::numeric_limits<int>::max()};
92 }
93
95
96 static constexpr std::time_t to_time_t(const time_point& tp) noexcept
97 {
98 using namespace std::chrono;
99 return duration_cast<seconds>(tp.time_since_epoch()).count();
100 }
101
102 static constexpr time_point from_time_t(std::time_t t) noexcept
103 {
104 using namespace std::chrono;
105 using FromPoint = std::chrono::time_point<WallClock, seconds>;
107 }
108};
109
112
113TOOLBOX_API std::ostream& operator<<(std::ostream& os, MonoTime t);
114TOOLBOX_API std::ostream& operator<<(std::ostream& os, WallTime t);
115
127 public:
128 static CyclTime current() noexcept { return {}; }
129 static CyclTime now() noexcept
130 {
131 time_ = Time::now();
132 return {}; // Empty tag.
133 }
135 static CyclTime now(WallTime wall_time) noexcept
136 {
137 time_ = Time::now(wall_time);
138 return {}; // Empty tag.
139 }
140 MonoTime mono_time() const noexcept { return time_.mono_time; }
141 WallTime wall_time() const noexcept { return time_.wall_time; }
142 void set_wall_time(WallTime wall_time) noexcept { time_.wall_time = wall_time; }
143
144 private:
145 CyclTime() = default;
146 struct Time {
147 static Time now() noexcept { return {MonoClock::now(), WallClock::now()}; };
148 static Time now(WallTime wall_time) noexcept { return {MonoClock::now(), wall_time}; };
149 MonoTime mono_time{};
150 WallTime wall_time{};
151 };
152 static thread_local Time time_;
153};
154
155template <typename RepT, typename PeriodT>
156constexpr bool is_zero(std::chrono::duration<RepT, PeriodT> d) noexcept
157{
158 return d == decltype(d){};
159}
160
161template <typename ClockT>
162constexpr bool is_zero(std::chrono::time_point<ClockT, Duration> t) noexcept
163{
164 return t == decltype(t){};
165}
166
167template <typename ClockT, typename DurationT>
168constexpr DurationT time_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
169{
170 using namespace std::chrono;
171 const Duration d{t.time_since_epoch()};
173}
174
175template <typename ClockT>
176constexpr std::int64_t ms_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
177{
178 using namespace std::chrono;
180}
181
182template <typename ClockT>
183constexpr std::int64_t us_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
184{
185 using namespace std::chrono;
187}
188
189template <typename ClockT>
190constexpr std::int64_t ns_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
191{
192 using namespace std::chrono;
193 const nanoseconds ns{t.time_since_epoch()};
194 return ns.count();
195}
196
197template <typename ClockT, typename RepT, typename PeriodT>
198constexpr auto to_time(std::chrono::duration<RepT, PeriodT> d) noexcept
199{
200 using namespace std::chrono;
201 return std::chrono::time_point<ClockT, Duration>{duration_cast<Duration>(d)};
202}
203
204template <typename ClockT>
205constexpr auto to_time(timeval tv) noexcept
206{
207 using namespace std::chrono;
208 return to_time<ClockT>(seconds{tv.tv_sec} + microseconds{tv.tv_usec});
209}
210
211template <typename ClockT>
212constexpr auto to_time(timespec ts) noexcept
213{
214 using namespace std::chrono;
215 return to_time<ClockT>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec});
216}
217
218template <typename RepT, typename PeriodT>
219constexpr timeval to_timeval(std::chrono::duration<RepT, PeriodT> d) noexcept
220{
221 using namespace std::chrono;
222 const auto us = duration_cast<microseconds>(d).count();
223 return {static_cast<time_t>(us / 1'000'000L), static_cast<suseconds_t>(us % 1'000'000L)};
224}
225
226template <typename ClockT>
227constexpr timeval to_timeval(std::chrono::time_point<ClockT, Duration> t) noexcept
228{
229 using namespace std::chrono;
231}
232
233template <typename RepT, typename PeriodT>
234constexpr timespec to_timespec(std::chrono::duration<RepT, PeriodT> d) noexcept
235{
236 using namespace std::chrono;
237 const auto ns = duration_cast<nanoseconds>(d).count();
238 return {static_cast<time_t>(ns / 1'000'000'000L), static_cast<long>(ns % 1'000'000'000L)};
239}
240
241template <typename ClockT>
242constexpr timespec to_timespec(std::chrono::time_point<ClockT, Duration> t) noexcept
243{
244 using namespace std::chrono;
246}
247
248template <typename DurationT>
249struct PutTime {
251 const char* fmt;
252};
253
254template <typename DurationT = Seconds>
255auto put_time(WallTime t, const char* fmt) noexcept
256{
257 return PutTime<DurationT>{t, fmt};
258}
259
260template <typename DurationT>
261std::ostream& operator<<(std::ostream& os, PutTime<DurationT> pt)
262{
263 const auto t = WallClock::to_time_t(pt.time);
264 struct tm gmt;
265 os << std::put_time(gmtime_r(&t, &gmt), pt.fmt);
266
267 if constexpr (std::is_same_v<DurationT, Nanos>) {
268 const auto ns = ns_since_epoch<WallClock>(pt.time);
269 boost::io::ios_fill_saver ifs{os};
270 boost::io::ios_width_saver iws{os};
271 os << '.' << std::setfill('0') << std::setw(9) << (ns % 1'000'000'000L);
272 } else if constexpr (std::is_same_v<DurationT, Micros>) {
273 const auto us = us_since_epoch<WallClock>(pt.time);
274 boost::io::ios_fill_saver ifs{os};
275 boost::io::ios_width_saver iws{os};
276 os << '.' << std::setfill('0') << std::setw(6) << (us % 1'000'000L);
277 } else if constexpr (std::is_same_v<DurationT, Millis>) {
278 const auto ms = ms_since_epoch<WallClock>(pt.time);
279 boost::io::ios_fill_saver ifs{os};
280 boost::io::ios_width_saver iws{os};
281 os << '.' << std::setfill('0') << std::setw(3) << (ms % 1'000L);
282 } else if constexpr (std::is_same_v<DurationT, Seconds>) {
283 } else {
284 static_assert(AlwaysFalse<DurationT>::value);
285 }
286 return os;
287}
288
289constexpr bool is_zero(timeval tv) noexcept
290{
291 return tv.tv_sec == 0 && tv.tv_usec == 0;
292}
293
294inline void clear(timeval& tv) noexcept
295{
296 tv.tv_sec = tv.tv_usec = 0;
297}
298
300{
301 timeval tv;
302 tv.tv_sec = lhs.tv_sec + rhs.tv_sec;
303 tv.tv_usec = lhs.tv_usec + rhs.tv_usec;
304 if (tv.tv_usec >= 1'000'000) {
305 ++tv.tv_sec;
306 tv.tv_usec -= 1'000'000;
307 }
308 return tv;
309}
310
312{
313 timeval tv;
314 tv.tv_sec = lhs.tv_sec - rhs.tv_sec;
315 tv.tv_usec = lhs.tv_usec - rhs.tv_usec;
316 if (tv.tv_usec < 0) {
317 --tv.tv_sec;
318 tv.tv_usec += 1'000'000;
319 }
320 return tv;
321}
322
323constexpr bool is_zero(timespec ts) noexcept
324{
325 return ts.tv_sec == 0 && ts.tv_nsec == 0;
326}
327
328inline void clear(timespec& ts) noexcept
329{
330 ts.tv_sec = ts.tv_nsec = 0;
331}
332
334{
335 timespec ts;
336 ts.tv_sec = lhs.tv_sec + rhs.tv_sec;
337 ts.tv_nsec = lhs.tv_nsec + rhs.tv_nsec;
338 if (ts.tv_nsec >= 1'000'000'000) {
339 ++ts.tv_sec;
340 ts.tv_nsec -= 1'000'000'000;
341 }
342 return ts;
343}
344
346{
347 timespec ts;
348 ts.tv_sec = lhs.tv_sec - rhs.tv_sec;
349 ts.tv_nsec = lhs.tv_nsec - rhs.tv_nsec;
350 if (ts.tv_nsec < 0) {
351 --ts.tv_sec;
352 ts.tv_nsec += 1'000'000'000;
353 }
354 return ts;
355}
356
360constexpr auto parse_nanos(std::string_view sv) noexcept
361{
362 // clang-format off
363 constexpr int c[] = {
364 0,
365 100'000'000,
366 10'000'000,
367 1'000'000,
368 100'000,
369 10'000,
370 1'000,
371 100,
372 10,
373 1
374 };
375 // clang-format on
376
377 // Truncate to ensure that we process no more than 9 decimal places.
378 sv = sv.substr(0, 9);
379 auto it = sv.begin(), end = sv.end();
380
381 int ns{0};
382 if (isdigit(*it)) {
383 ns = *it++ - '0';
384 while (it != end && isdigit(*it)) {
385 ns *= 10;
386 ns += *it++ - '0';
387 }
388 }
389 return Nanos{ns * c[it - sv.begin()]};
390}
391static_assert(parse_nanos("000000001") == 1ns);
392
399constexpr std::optional<Nanos> parse_time_only(std::string_view sv) noexcept
400{
401 using namespace std::chrono;
402
403 // clang-format off
404 if (sv.size() < 8
405 || !isdigit(sv[0])
406 || !isdigit(sv[1])
407 || sv[2] != ':'
408 || !isdigit(sv[3])
409 || !isdigit(sv[4])
410 || sv[5] != ':'
411 || !isdigit(sv[6])
412 || !isdigit(sv[7])) {
413 // Invalid format.
414 return {};
415 }
416 // clang-format on
417 const hours h{(sv[0] - '0') * 10 + sv[1] - '0'};
418 const minutes m{(sv[3] - '0') * 10 + sv[4] - '0'};
419 const seconds s{(sv[6] - '0') * 10 + sv[7] - '0'};
420 Nanos ns{h + m + s};
421 if (sv.size() > 8) {
422 if (sv[8] != '.') {
423 // Invalid delimiter.
424 return {};
425 }
426 ns += parse_nanos(sv.substr(9));
427 }
428 return ns;
429}
430static_assert(*parse_time_only("12:00:00"sv) == 12h);
431
432} // namespace sys
433inline namespace util {
434
435template <typename RepT, typename PeriodT>
436struct TypeTraits<std::chrono::duration<RepT, PeriodT>> {
437 static auto from_string(std::string_view sv) noexcept
438 {
439 using namespace std::chrono;
441 using Rep = typename Duration::rep;
443 }
444 static auto from_string(const std::string& s) noexcept
445 {
446 return from_string(std::string_view{s});
447 }
448};
449
450template <>
452 static auto from_string(std::string_view sv) noexcept
453 {
455 }
456 static auto from_string(const std::string& s) noexcept
457 {
458 return from_string(std::string_view{s});
459 }
460};
461
462} // namespace util
463} // namespace toolbox
464
465#endif // TOOLBOX_SYS_TIME_HPP
#define TOOLBOX_API
Definition Config.h:39
MonoTime mono_time() const noexcept
Definition Time.hpp:140
static CyclTime now() noexcept
Definition Time.hpp:129
static CyclTime current() noexcept
Definition Time.hpp:128
WallTime wall_time() const noexcept
Definition Time.hpp:141
static CyclTime now(WallTime wall_time) noexcept
This overload allows users to override wall-clock time.
Definition Time.hpp:135
void set_wall_time(WallTime wall_time) noexcept
Definition Time.hpp:142
STL namespace.
ostream & operator<<(ostream &os, const pair< T, U > &p)
Definition Parser.ut.cpp:29
constexpr bool is_zero(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:156
constexpr std::int64_t us_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:183
constexpr std::optional< Nanos > parse_time_only(std::string_view sv) noexcept
Definition Time.hpp:399
constexpr std::int64_t ms_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:176
constexpr DurationT time_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:168
constexpr std::int64_t ns_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:190
auto put_time(WallTime t, const char *fmt) noexcept
Definition Time.hpp:255
timeval operator-(timeval lhs, timeval rhs) noexcept
Definition Time.hpp:311
std::chrono::microseconds Micros
Definition Time.hpp:36
std::chrono::milliseconds Millis
Definition Time.hpp:35
WallClock::time_point WallTime
Definition Time.hpp:111
constexpr auto parse_nanos(std::string_view sv) noexcept
Definition Time.hpp:360
TOOLBOX_WEAK Nanos get_time(clockid_t clock_id) noexcept
Definition Time.cpp:25
MonoClock::time_point MonoTime
Definition Time.hpp:110
std::chrono::seconds Seconds
Definition Time.hpp:33
std::chrono::nanoseconds Nanos
Definition Time.hpp:37
constexpr auto to_time(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:198
constexpr timeval to_timeval(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:219
constexpr timespec to_timespec(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:234
Nanos NanoTime
Definition Time.hpp:39
std::chrono::duration< int64_t, std::deci > Decis
Definition Time.hpp:34
timeval operator+(timeval lhs, timeval rhs) noexcept
Definition Time.hpp:299
Nanos Duration
Definition Time.hpp:40
void clear(timeval &tv) noexcept
Definition Time.hpp:294
constexpr bool isdigit(int c) noexcept
Definition Utility.hpp:38
std::string_view sv
Definition Tokeniser.hpp:26
constexpr auto bind() noexcept
Definition Slot.hpp:92
static constexpr int Id
Definition Time.hpp:50
std::chrono::time_point< MonoClock, Duration > time_point
Definition Time.hpp:48
Duration::rep rep
Definition Time.hpp:47
static constexpr std::time_t to_time_t(const time_point &tp) noexcept
Definition Time.hpp:63
static time_point now() noexcept
Definition Time.hpp:61
static constexpr time_point max() noexcept
Definition Time.hpp:53
static constexpr time_point from_time_t(std::time_t t) noexcept
Definition Time.hpp:69
Duration::period period
Definition Time.hpp:46
static constexpr bool is_steady
Definition Time.hpp:51
const char * fmt
Definition Time.hpp:251
static constexpr time_point from_time_t(std::time_t t) noexcept
Definition Time.hpp:102
std::chrono::time_point< WallClock, Duration > time_point
Definition Time.hpp:81
Duration::rep rep
Definition Time.hpp:80
static constexpr bool is_steady
Definition Time.hpp:84
static constexpr std::time_t to_time_t(const time_point &tp) noexcept
Definition Time.hpp:96
static constexpr time_point max() noexcept
Definition Time.hpp:86
static constexpr int Id
Definition Time.hpp:83
static time_point now() noexcept
Definition Time.hpp:94
Duration::period period
Definition Time.hpp:79
static auto from_string(std::string_view sv) noexcept
Definition Time.hpp:452
static auto from_string(const std::string &s) noexcept
Definition Time.hpp:456
static auto from_string(std::string_view sv) noexcept
Definition Time.hpp:437
static auto from_string(const std::string &s) noexcept
Definition Time.hpp:444
static constexpr auto from_string(StringT &&s) noexcept(noexcept(ValueT{s}))