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
24
25#include <chrono>
26#include <format>
27#include <iomanip>
28#include <optional>
29
30namespace toolbox {
31inline namespace sys {
32using namespace std::literals::chrono_literals;
33using namespace std::literals::string_view_literals;
34
35using Seconds = std::chrono::seconds;
36using Decis = std::chrono::duration<int64_t, std::deci>;
37using Millis = std::chrono::milliseconds;
38using Micros = std::chrono::microseconds;
39using Nanos = std::chrono::nanoseconds;
40
43
45
46struct MonoClock {
48 using period = Duration::period;
49 using rep = Duration::rep;
50 using time_point = std::chrono::time_point<MonoClock, Duration>;
51
52 static constexpr int Id{CLOCK_MONOTONIC};
53 static constexpr bool is_steady{true};
54
55 static constexpr time_point max() noexcept
56 {
57 using namespace std::chrono;
58 using FromPoint = std::chrono::time_point<MonoClock, seconds>;
59 constexpr seconds secs{std::numeric_limits<int>::max()};
61 }
62
64
65 static constexpr std::time_t to_time_t(const time_point& tp) noexcept
66 {
67 using namespace std::chrono;
68 return duration_cast<seconds>(tp.time_since_epoch()).count();
69 }
70
71 static constexpr time_point from_time_t(std::time_t t) noexcept
72 {
73 using namespace std::chrono;
74 using FromPoint = std::chrono::time_point<MonoClock, seconds>;
76 }
77};
78
79struct WallClock {
81 using period = Duration::period;
82 using rep = Duration::rep;
83 using time_point = std::chrono::time_point<WallClock, Duration>;
84
85 static constexpr int Id{CLOCK_REALTIME};
86 static constexpr bool is_steady{false};
87
88 static constexpr time_point max() noexcept
89 {
90 using namespace std::chrono;
91 using FromPoint = std::chrono::time_point<WallClock, seconds>;
92 constexpr seconds secs{std::numeric_limits<int>::max()};
94 }
95
97
98 static constexpr std::time_t to_time_t(const time_point& tp) noexcept
99 {
100 using namespace std::chrono;
101 return duration_cast<seconds>(tp.time_since_epoch()).count();
102 }
103
104 static constexpr time_point from_time_t(std::time_t t) noexcept
105 {
106 using namespace std::chrono;
107 using FromPoint = std::chrono::time_point<WallClock, seconds>;
109 }
110};
111
114
126 public:
127 static CyclTime current() noexcept { return {}; }
128 static CyclTime now() noexcept
129 {
130 time_ = Time::now();
131 return {}; // Empty tag.
132 }
134 static CyclTime now(WallTime wall_time) noexcept
135 {
136 time_ = Time::now(wall_time);
137 return {}; // Empty tag.
138 }
139 MonoTime mono_time() const noexcept { return time_.mono_time; }
140 WallTime wall_time() const noexcept { return time_.wall_time; }
141 void set_wall_time(WallTime wall_time) noexcept { time_.wall_time = wall_time; }
142
143 private:
144 CyclTime() = default;
145 struct Time {
146 static Time now() noexcept { return {MonoClock::now(), WallClock::now()}; };
147 static Time now(WallTime wall_time) noexcept { return {MonoClock::now(), wall_time}; };
148 MonoTime mono_time{};
149 WallTime wall_time{};
150 };
151 static thread_local Time time_;
152};
153
154template <typename RepT, typename PeriodT>
155constexpr bool is_zero(std::chrono::duration<RepT, PeriodT> d) noexcept
156{
157 return d == decltype(d){};
158}
159
160template <typename ClockT>
161constexpr bool is_zero(std::chrono::time_point<ClockT, Duration> t) noexcept
162{
163 return t == decltype(t){};
164}
165
166template <typename ClockT, typename DurationT>
167constexpr DurationT time_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
168{
169 using namespace std::chrono;
170 const Duration d{t.time_since_epoch()};
172}
173
174template <typename ClockT>
175constexpr std::int64_t ms_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
176{
177 using namespace std::chrono;
179}
180
181template <typename ClockT>
182constexpr std::int64_t us_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
183{
184 using namespace std::chrono;
186}
187
188template <typename ClockT>
189constexpr std::int64_t ns_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
190{
191 using namespace std::chrono;
192 const nanoseconds ns{t.time_since_epoch()};
193 return ns.count();
194}
195
196template <typename ClockT, typename RepT, typename PeriodT>
197constexpr auto to_time(std::chrono::duration<RepT, PeriodT> d) noexcept
198{
199 using namespace std::chrono;
200 return std::chrono::time_point<ClockT, Duration>{duration_cast<Duration>(d)};
201}
202
203template <typename ClockT>
204constexpr auto to_time(timeval tv) noexcept
205{
206 using namespace std::chrono;
207 return to_time<ClockT>(seconds{tv.tv_sec} + microseconds{tv.tv_usec});
208}
209
210template <typename ClockT>
211constexpr auto to_time(timespec ts) noexcept
212{
213 using namespace std::chrono;
214 return to_time<ClockT>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec});
215}
216
217template <typename RepT, typename PeriodT>
218constexpr timeval to_timeval(std::chrono::duration<RepT, PeriodT> d) noexcept
219{
220 using namespace std::chrono;
221 const auto us = duration_cast<microseconds>(d).count();
222 return {static_cast<time_t>(us / 1'000'000L), static_cast<suseconds_t>(us % 1'000'000L)};
223}
224
225template <typename ClockT>
226constexpr timeval to_timeval(std::chrono::time_point<ClockT, Duration> t) noexcept
227{
228 using namespace std::chrono;
230}
231
232template <typename RepT, typename PeriodT>
233constexpr timespec to_timespec(std::chrono::duration<RepT, PeriodT> d) noexcept
234{
235 using namespace std::chrono;
236 const auto ns = duration_cast<nanoseconds>(d).count();
237 return {static_cast<time_t>(ns / 1'000'000'000L), static_cast<long>(ns % 1'000'000'000L)};
238}
239
240template <typename ClockT>
241constexpr timespec to_timespec(std::chrono::time_point<ClockT, Duration> t) noexcept
242{
243 using namespace std::chrono;
245}
246
247template <typename StreamT>
248 requires Streamable<StreamT>
250{
251 os << ns_since_epoch(t);
252 return os;
253}
254
255template <typename StreamT>
256 requires Streamable<StreamT>
258{
259 os << ns_since_epoch(t);
260 return os;
261}
262
263template <typename DurationT>
264struct PutTime {
266 const char* fmt;
267};
268
269template <typename DurationT = Seconds>
270auto put_time(WallTime t, const char* fmt) noexcept
271{
272 return PutTime<DurationT>{t, fmt};
273}
274
275template <typename DurationT, typename StreamT>
276 requires Streamable<StreamT>
278{
279 const auto t = WallClock::to_time_t(pt.time);
280 struct tm gmt;
281 os << to_string(std::put_time(gmtime_r(&t, &gmt), pt.fmt));
282
283 if constexpr (std::is_same_v<DurationT, Nanos>) {
284 const auto ns = ns_since_epoch<WallClock>(pt.time);
285 os << '.' << std::format("{:0>9}", ns % 1'000'000'000L);
286 } else if constexpr (std::is_same_v<DurationT, Micros>) {
287 const auto us = us_since_epoch<WallClock>(pt.time);
288 os << '.' << std::format("{:0>6}", us % 1'000'000L);
289 } else if constexpr (std::is_same_v<DurationT, Millis>) {
290 const auto ms = ms_since_epoch<WallClock>(pt.time);
291 os << '.' << std::format("{:0>3}", ms % 1'000L);
292 } else if constexpr (std::is_same_v<DurationT, Seconds>) {
293 } else {
294 static_assert(AlwaysFalse<DurationT>::value);
295 }
296 return os;
297}
298
299constexpr bool is_zero(timeval tv) noexcept
300{
301 return tv.tv_sec == 0 && tv.tv_usec == 0;
302}
303
304inline void clear(timeval& tv) noexcept
305{
306 tv.tv_sec = tv.tv_usec = 0;
307}
308
310{
311 timeval tv;
312 tv.tv_sec = lhs.tv_sec + rhs.tv_sec;
313 tv.tv_usec = lhs.tv_usec + rhs.tv_usec;
314 if (tv.tv_usec >= 1'000'000) {
315 ++tv.tv_sec;
316 tv.tv_usec -= 1'000'000;
317 }
318 return tv;
319}
320
322{
323 timeval tv;
324 tv.tv_sec = lhs.tv_sec - rhs.tv_sec;
325 tv.tv_usec = lhs.tv_usec - rhs.tv_usec;
326 if (tv.tv_usec < 0) {
327 --tv.tv_sec;
328 tv.tv_usec += 1'000'000;
329 }
330 return tv;
331}
332
333constexpr bool is_zero(timespec ts) noexcept
334{
335 return ts.tv_sec == 0 && ts.tv_nsec == 0;
336}
337
338inline void clear(timespec& ts) noexcept
339{
340 ts.tv_sec = ts.tv_nsec = 0;
341}
342
344{
345 timespec ts;
346 ts.tv_sec = lhs.tv_sec + rhs.tv_sec;
347 ts.tv_nsec = lhs.tv_nsec + rhs.tv_nsec;
348 if (ts.tv_nsec >= 1'000'000'000) {
349 ++ts.tv_sec;
350 ts.tv_nsec -= 1'000'000'000;
351 }
352 return ts;
353}
354
356{
357 timespec ts;
358 ts.tv_sec = lhs.tv_sec - rhs.tv_sec;
359 ts.tv_nsec = lhs.tv_nsec - rhs.tv_nsec;
360 if (ts.tv_nsec < 0) {
361 --ts.tv_sec;
362 ts.tv_nsec += 1'000'000'000;
363 }
364 return ts;
365}
366
370constexpr auto parse_nanos(std::string_view sv) noexcept
371{
372 // clang-format off
373 constexpr int c[] = {
374 0,
375 100'000'000,
376 10'000'000,
377 1'000'000,
378 100'000,
379 10'000,
380 1'000,
381 100,
382 10,
383 1
384 };
385 // clang-format on
386
387 // Truncate to ensure that we process no more than 9 decimal places.
388 sv = sv.substr(0, 9);
389 auto it = sv.begin(), end = sv.end();
390
391 int ns{0};
392 if (isdigit(*it)) {
393 ns = *it++ - '0';
394 while (it != end && isdigit(*it)) {
395 ns *= 10;
396 ns += *it++ - '0';
397 }
398 }
399 return Nanos{ns * c[it - sv.begin()]};
400}
401static_assert(parse_nanos("000000001") == 1ns);
402
409constexpr std::optional<Nanos> parse_time_only(std::string_view sv) noexcept
410{
411 using namespace std::chrono;
412
413 // clang-format off
414 if (sv.size() < 8
415 || !isdigit(sv[0])
416 || !isdigit(sv[1])
417 || sv[2] != ':'
418 || !isdigit(sv[3])
419 || !isdigit(sv[4])
420 || sv[5] != ':'
421 || !isdigit(sv[6])
422 || !isdigit(sv[7])) {
423 // Invalid format.
424 return {};
425 }
426 // clang-format on
427 const hours h{(sv[0] - '0') * 10 + sv[1] - '0'};
428 const minutes m{(sv[3] - '0') * 10 + sv[4] - '0'};
429 const seconds s{(sv[6] - '0') * 10 + sv[7] - '0'};
430 Nanos ns{h + m + s};
431 if (sv.size() > 8) {
432 if (sv[8] != '.') {
433 // Invalid delimiter.
434 return {};
435 }
436 ns += parse_nanos(sv.substr(9));
437 }
438 return ns;
439}
440static_assert(*parse_time_only("12:00:00"sv) == 12h);
441
442} // namespace sys
443inline namespace util {
444
445template <typename RepT, typename PeriodT>
446struct TypeTraits<std::chrono::duration<RepT, PeriodT>> {
447 static auto from_string(std::string_view sv) noexcept
448 {
449 using namespace std::chrono;
451 using Rep = typename Duration::rep;
453 }
454 static auto from_string(const std::string& s) noexcept
455 {
456 return from_string(std::string_view{s});
457 }
458};
459
460template <>
462 static auto from_string(std::string_view sv) noexcept
463 {
465 }
466 static auto from_string(const std::string& s) noexcept
467 {
468 return from_string(std::string_view{s});
469 }
470};
471
475 public:
477 : cooldown_interval_(cooldown_interval) {}
478
479 template <typename Callable>
481 {
482 if (duration_cast<Seconds>(now - last_time_invoked_) >= cooldown_interval_) {
483 last_time_invoked_ = now;
484 std::forward<Callable>(callable)();
485 }
486 }
487
488 private:
489 const Seconds cooldown_interval_{};
490 MonoTime last_time_invoked_{};
491};
492
493} // namespace util
494} // namespace toolbox
495
496#endif // TOOLBOX_SYS_TIME_HPP
#define TOOLBOX_API
Definition Config.h:39
MonoTime mono_time() const noexcept
Definition Time.hpp:139
static CyclTime now() noexcept
Definition Time.hpp:128
static CyclTime current() noexcept
Definition Time.hpp:127
WallTime wall_time() const noexcept
Definition Time.hpp:140
static CyclTime now(WallTime wall_time) noexcept
This overload allows users to override wall-clock time.
Definition Time.hpp:134
void set_wall_time(WallTime wall_time) noexcept
Definition Time.hpp:141
void operator()(MonoTime now, Callable &&callable)
Definition Time.hpp:480
ThrottledInvoker(Seconds cooldown_interval)
Definition Time.hpp:476
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:155
constexpr std::int64_t us_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:182
constexpr std::optional< Nanos > parse_time_only(std::string_view sv) noexcept
Definition Time.hpp:409
constexpr std::int64_t ms_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:175
constexpr DurationT time_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:167
constexpr std::int64_t ns_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:189
auto put_time(WallTime t, const char *fmt) noexcept
Definition Time.hpp:270
timeval operator-(timeval lhs, timeval rhs) noexcept
Definition Time.hpp:321
std::chrono::microseconds Micros
Definition Time.hpp:38
std::chrono::milliseconds Millis
Definition Time.hpp:37
WallClock::time_point WallTime
Definition Time.hpp:113
constexpr auto parse_nanos(std::string_view sv) noexcept
Definition Time.hpp:370
TOOLBOX_WEAK Nanos get_time(clockid_t clock_id) noexcept
Definition Time.cpp:25
MonoClock::time_point MonoTime
Definition Time.hpp:112
std::chrono::seconds Seconds
Definition Time.hpp:35
std::chrono::nanoseconds Nanos
Definition Time.hpp:39
constexpr auto to_time(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:197
constexpr timeval to_timeval(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:218
constexpr timespec to_timespec(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:233
Nanos NanoTime
Definition Time.hpp:41
std::chrono::duration< int64_t, std::deci > Decis
Definition Time.hpp:36
timeval operator+(timeval lhs, timeval rhs) noexcept
Definition Time.hpp:309
Nanos Duration
Definition Time.hpp:42
void clear(timeval &tv) noexcept
Definition Time.hpp:304
std::string to_string(ValueT &&val)
Definition String.hpp:55
constexpr bool isdigit(int c) noexcept
Definition Utility.hpp:40
std::string_view sv
Definition Tokeniser.hpp:26
constexpr auto bind() noexcept
Definition Slot.hpp:97
static constexpr int Id
Definition Time.hpp:52
std::chrono::time_point< MonoClock, Duration > time_point
Definition Time.hpp:50
Duration::rep rep
Definition Time.hpp:49
static constexpr std::time_t to_time_t(const time_point &tp) noexcept
Definition Time.hpp:65
static time_point now() noexcept
Definition Time.hpp:63
static constexpr time_point max() noexcept
Definition Time.hpp:55
static constexpr time_point from_time_t(std::time_t t) noexcept
Definition Time.hpp:71
Duration::period period
Definition Time.hpp:48
static constexpr bool is_steady
Definition Time.hpp:53
const char * fmt
Definition Time.hpp:266
static constexpr time_point from_time_t(std::time_t t) noexcept
Definition Time.hpp:104
std::chrono::time_point< WallClock, Duration > time_point
Definition Time.hpp:83
Duration::rep rep
Definition Time.hpp:82
static constexpr bool is_steady
Definition Time.hpp:86
static constexpr std::time_t to_time_t(const time_point &tp) noexcept
Definition Time.hpp:98
static constexpr time_point max() noexcept
Definition Time.hpp:88
static constexpr int Id
Definition Time.hpp:85
static time_point now() noexcept
Definition Time.hpp:96
Duration::period period
Definition Time.hpp:81
static auto from_string(std::string_view sv) noexcept
Definition Time.hpp:462
static auto from_string(const std::string &s) noexcept
Definition Time.hpp:466
static auto from_string(std::string_view sv) noexcept
Definition Time.hpp:447
static auto from_string(const std::string &s) noexcept
Definition Time.hpp:454
static constexpr auto from_string(StringT &&s) noexcept(noexcept(ValueT{s}))