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
23
24#include <chrono>
25#include <format>
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
124 public:
125 static CyclTime current() noexcept { return {}; }
126 static CyclTime now() noexcept
127 {
128 time_ = Time::now();
129 return {}; // Empty tag.
130 }
132 static CyclTime now(WallTime wall_time) noexcept
133 {
134 time_ = Time::now(wall_time);
135 return {}; // Empty tag.
136 }
137 MonoTime mono_time() const noexcept { return time_.mono_time; }
138 WallTime wall_time() const noexcept { return time_.wall_time; }
139 void set_wall_time(WallTime wall_time) noexcept { time_.wall_time = wall_time; }
140
141 private:
142 CyclTime() = default;
143 struct Time {
144 static Time now() noexcept { return {MonoClock::now(), WallClock::now()}; };
145 static Time now(WallTime wall_time) noexcept { return {MonoClock::now(), wall_time}; };
146 MonoTime mono_time{};
147 WallTime wall_time{};
148 };
149 static thread_local Time time_;
150};
151
152template <typename RepT, typename PeriodT>
153constexpr bool is_zero(std::chrono::duration<RepT, PeriodT> d) noexcept
154{
155 return d == decltype(d){};
156}
157
158template <typename ClockT>
159constexpr bool is_zero(std::chrono::time_point<ClockT, Duration> t) noexcept
160{
161 return t == decltype(t){};
162}
163
164template <typename ClockT, typename DurationT>
165constexpr DurationT time_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
166{
167 using namespace std::chrono;
168 const Duration d{t.time_since_epoch()};
170}
171
172template <typename ClockT>
173constexpr std::int64_t ms_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
174{
175 using namespace std::chrono;
177}
178
179template <typename ClockT>
180constexpr std::int64_t us_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
181{
182 using namespace std::chrono;
184}
185
186template <typename ClockT>
187constexpr std::int64_t ns_since_epoch(std::chrono::time_point<ClockT, Duration> t) noexcept
188{
189 using namespace std::chrono;
190 const nanoseconds ns{t.time_since_epoch()};
191 return ns.count();
192}
193
194template <typename ClockT, typename RepT, typename PeriodT>
195constexpr auto to_time(std::chrono::duration<RepT, PeriodT> d) noexcept
196{
197 using namespace std::chrono;
198 return std::chrono::time_point<ClockT, Duration>{duration_cast<Duration>(d)};
199}
200
201template <typename ClockT>
202constexpr auto to_time(timeval tv) noexcept
203{
204 using namespace std::chrono;
205 return to_time<ClockT>(seconds{tv.tv_sec} + microseconds{tv.tv_usec});
206}
207
208template <typename ClockT>
209constexpr auto to_time(timespec ts) noexcept
210{
211 using namespace std::chrono;
212 return to_time<ClockT>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec});
213}
214
215template <typename RepT, typename PeriodT>
216constexpr timeval to_timeval(std::chrono::duration<RepT, PeriodT> d) noexcept
217{
218 using namespace std::chrono;
219 const auto us = duration_cast<microseconds>(d).count();
220 return {static_cast<time_t>(us / 1'000'000L), static_cast<suseconds_t>(us % 1'000'000L)};
221}
222
223template <typename ClockT>
224constexpr timeval to_timeval(std::chrono::time_point<ClockT, Duration> t) noexcept
225{
226 using namespace std::chrono;
228}
229
230template <typename RepT, typename PeriodT>
231constexpr timespec to_timespec(std::chrono::duration<RepT, PeriodT> d) noexcept
232{
233 using namespace std::chrono;
234 const auto ns = duration_cast<nanoseconds>(d).count();
235 return {static_cast<time_t>(ns / 1'000'000'000L), static_cast<long>(ns % 1'000'000'000L)};
236}
237
238template <typename ClockT>
239constexpr timespec to_timespec(std::chrono::time_point<ClockT, Duration> t) noexcept
240{
241 using namespace std::chrono;
243}
244
245template <typename StreamT>
246 requires Streamable<StreamT>
248{
249 os << ns_since_epoch(t);
250 return os;
251}
252
253template <typename StreamT>
254 requires Streamable<StreamT>
256{
257 os << ns_since_epoch(t);
258 return os;
259}
260
261template <typename DurationT>
262struct PutTime {
264 const char* fmt;
265};
266
267template <typename DurationT = Seconds>
268auto put_time(WallTime t, const char* fmt) noexcept
269{
270 return PutTime<DurationT>{t, fmt};
271}
272
273template <typename DurationT, typename StreamT>
274 requires Streamable<StreamT>
276{
277 const auto t = WallClock::to_time_t(pt.time);
278 struct tm gmt;
279 os << std::put_time(gmtime_r(&t, &gmt), pt.fmt);
280
281 if constexpr (std::is_same_v<DurationT, Nanos>) {
282 const auto ns = ns_since_epoch<WallClock>(pt.time);
283 os << '.' << std::format("{:0>9}", ns % 1'000'000'000L);
284 } else if constexpr (std::is_same_v<DurationT, Micros>) {
285 const auto us = us_since_epoch<WallClock>(pt.time);
286 os << '.' << std::format("{:0>6}", us % 1'000'000L);
287 } else if constexpr (std::is_same_v<DurationT, Millis>) {
288 const auto ms = ms_since_epoch<WallClock>(pt.time);
289 os << '.' << std::format("{:0>3}", ms % 1'000L);
290 } else if constexpr (std::is_same_v<DurationT, Seconds>) {
291 } else {
292 static_assert(AlwaysFalse<DurationT>::value);
293 }
294 return os;
295}
296
297constexpr bool is_zero(timeval tv) noexcept
298{
299 return tv.tv_sec == 0 && tv.tv_usec == 0;
300}
301
302inline void clear(timeval& tv) noexcept
303{
304 tv.tv_sec = tv.tv_usec = 0;
305}
306
308{
309 timeval tv;
310 tv.tv_sec = lhs.tv_sec + rhs.tv_sec;
311 tv.tv_usec = lhs.tv_usec + rhs.tv_usec;
312 if (tv.tv_usec >= 1'000'000) {
313 ++tv.tv_sec;
314 tv.tv_usec -= 1'000'000;
315 }
316 return tv;
317}
318
320{
321 timeval tv;
322 tv.tv_sec = lhs.tv_sec - rhs.tv_sec;
323 tv.tv_usec = lhs.tv_usec - rhs.tv_usec;
324 if (tv.tv_usec < 0) {
325 --tv.tv_sec;
326 tv.tv_usec += 1'000'000;
327 }
328 return tv;
329}
330
331constexpr bool is_zero(timespec ts) noexcept
332{
333 return ts.tv_sec == 0 && ts.tv_nsec == 0;
334}
335
336inline void clear(timespec& ts) noexcept
337{
338 ts.tv_sec = ts.tv_nsec = 0;
339}
340
342{
343 timespec ts;
344 ts.tv_sec = lhs.tv_sec + rhs.tv_sec;
345 ts.tv_nsec = lhs.tv_nsec + rhs.tv_nsec;
346 if (ts.tv_nsec >= 1'000'000'000) {
347 ++ts.tv_sec;
348 ts.tv_nsec -= 1'000'000'000;
349 }
350 return ts;
351}
352
354{
355 timespec ts;
356 ts.tv_sec = lhs.tv_sec - rhs.tv_sec;
357 ts.tv_nsec = lhs.tv_nsec - rhs.tv_nsec;
358 if (ts.tv_nsec < 0) {
359 --ts.tv_sec;
360 ts.tv_nsec += 1'000'000'000;
361 }
362 return ts;
363}
364
368constexpr auto parse_nanos(std::string_view sv) noexcept
369{
370 // clang-format off
371 constexpr int c[] = {
372 0,
373 100'000'000,
374 10'000'000,
375 1'000'000,
376 100'000,
377 10'000,
378 1'000,
379 100,
380 10,
381 1
382 };
383 // clang-format on
384
385 // Truncate to ensure that we process no more than 9 decimal places.
386 sv = sv.substr(0, 9);
387 auto it = sv.begin(), end = sv.end();
388
389 int ns{0};
390 if (isdigit(*it)) {
391 ns = *it++ - '0';
392 while (it != end && isdigit(*it)) {
393 ns *= 10;
394 ns += *it++ - '0';
395 }
396 }
397 return Nanos{ns * c[it - sv.begin()]};
398}
399static_assert(parse_nanos("000000001") == 1ns);
400
407constexpr std::optional<Nanos> parse_time_only(std::string_view sv) noexcept
408{
409 using namespace std::chrono;
410
411 // clang-format off
412 if (sv.size() < 8
413 || !isdigit(sv[0])
414 || !isdigit(sv[1])
415 || sv[2] != ':'
416 || !isdigit(sv[3])
417 || !isdigit(sv[4])
418 || sv[5] != ':'
419 || !isdigit(sv[6])
420 || !isdigit(sv[7])) {
421 // Invalid format.
422 return {};
423 }
424 // clang-format on
425 const hours h{(sv[0] - '0') * 10 + sv[1] - '0'};
426 const minutes m{(sv[3] - '0') * 10 + sv[4] - '0'};
427 const seconds s{(sv[6] - '0') * 10 + sv[7] - '0'};
428 Nanos ns{h + m + s};
429 if (sv.size() > 8) {
430 if (sv[8] != '.') {
431 // Invalid delimiter.
432 return {};
433 }
434 ns += parse_nanos(sv.substr(9));
435 }
436 return ns;
437}
438static_assert(*parse_time_only("12:00:00"sv) == 12h);
439
440} // namespace sys
441inline namespace util {
442
443template <typename RepT, typename PeriodT>
444struct TypeTraits<std::chrono::duration<RepT, PeriodT>> {
445 static auto from_string(std::string_view sv) noexcept
446 {
447 using namespace std::chrono;
449 using Rep = typename Duration::rep;
451 }
452 static auto from_string(const std::string& s) noexcept
453 {
454 return from_string(std::string_view{s});
455 }
456};
457
458template <>
460 static auto from_string(std::string_view sv) noexcept
461 {
463 }
464 static auto from_string(const std::string& s) noexcept
465 {
466 return from_string(std::string_view{s});
467 }
468};
469
470} // namespace util
471} // namespace toolbox
472
473#endif // TOOLBOX_SYS_TIME_HPP
#define TOOLBOX_API
Definition Config.h:39
MonoTime mono_time() const noexcept
Definition Time.hpp:137
static CyclTime now() noexcept
Definition Time.hpp:126
static CyclTime current() noexcept
Definition Time.hpp:125
WallTime wall_time() const noexcept
Definition Time.hpp:138
static CyclTime now(WallTime wall_time) noexcept
This overload allows users to override wall-clock time.
Definition Time.hpp:132
void set_wall_time(WallTime wall_time) noexcept
Definition Time.hpp:139
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:153
constexpr std::int64_t us_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:180
constexpr std::optional< Nanos > parse_time_only(std::string_view sv) noexcept
Definition Time.hpp:407
constexpr std::int64_t ms_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:173
constexpr DurationT time_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:165
constexpr std::int64_t ns_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:187
auto put_time(WallTime t, const char *fmt) noexcept
Definition Time.hpp:268
timeval operator-(timeval lhs, timeval rhs) noexcept
Definition Time.hpp:319
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:368
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:195
constexpr timeval to_timeval(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:216
constexpr timespec to_timespec(std::chrono::duration< RepT, PeriodT > d) noexcept
Definition Time.hpp:231
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:307
Nanos Duration
Definition Time.hpp:40
void clear(timeval &tv) noexcept
Definition Time.hpp:302
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: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:264
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:460
static auto from_string(const std::string &s) noexcept
Definition Time.hpp:464
static auto from_string(std::string_view sv) noexcept
Definition Time.hpp:445
static auto from_string(const std::string &s) noexcept
Definition Time.hpp:452
static constexpr auto from_string(StringT &&s) noexcept(noexcept(ValueT{s}))