microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
posix_time.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
10#include "../microfmt.hpp"
11#include <cstdint>
12#include <type_traits>
13
14namespace microfmt {
15
16// ============================================================================
17// Structural Traits for POSIX Time Types
18// ============================================================================
19
20namespace detail {
21
26template <typename T, typename = void> struct is_timespec_like : std::false_type {};
27
33template <typename T>
34struct is_timespec_like<T, std::void_t<decltype(std::declval<T>().tv_sec), decltype(std::declval<T>().tv_nsec)>>
35 : std::true_type {};
36
41template <typename T, typename = void> struct is_timeval_like : std::false_type {};
42
48template <typename T>
49struct is_timeval_like<T, std::void_t<decltype(std::declval<T>().tv_sec), decltype(std::declval<T>().tv_usec)>>
50 : std::true_type {};
51
52} // namespace detail
53
58template <typename T>
60
65template <typename T>
67
68// ============================================================================
69// Formatter for struct timespec (.tv_sec, .tv_nsec)
70// ============================================================================
71
80template <typename T> struct formatter<T, std::enable_if_t<is_timespec_v<T>>> {
84 uint8_t precision{9}; // default: 9 digits (nanoseconds)
88 bool show_unit{true}; // 'r' -> raw numbers without 's' suffix
89
94 constexpr void parse(format_parse_context &ctx) noexcept {
95 auto spec = ctx.spec();
96 if (!spec.empty() && spec.front() == ':') {
97 spec.remove_prefix(1);
98 }
99 for (char c : spec) {
100 if (c == 'm' || c == '3')
101 precision = 3;
102 else if (c == 'u' || c == '6')
103 precision = 6;
104 else if (c == 'n' || c == '9')
105 precision = 9;
106 else if (c == 'r' || c == 'R')
107 show_unit = false;
108 }
109 }
110
116 // time-field arithmetic converts between C/POSIX integer widths whose ranges are bounded by the format.
117 RELOCO_BEGIN_UNSAFE_CODE
118 void format(const T &ts, const sink &out) const noexcept {
119 int64_t sec = static_cast<int64_t>(ts.tv_sec);
120 uint32_t nsec = static_cast<uint32_t>(ts.tv_nsec);
121
122 if (sec < 0) {
123 out.put('-');
124 detail::format_unsigned<detail::radix::decimal>(out, static_cast<uint64_t>(-sec), false, 0);
125 } else {
126 detail::format_unsigned<detail::radix::decimal>(out, static_cast<uint64_t>(sec), false, 0);
127 }
128
129 out.put('.');
130 if (precision == 3) {
131 detail::format_unsigned<detail::radix::decimal>(out, nsec / 1'000'000, false, 3);
132 } else if (precision == 6) {
133 detail::format_unsigned<detail::radix::decimal>(out, nsec / 1'000, false, 6);
134 } else {
135 detail::format_unsigned<detail::radix::decimal>(out, nsec, false, 9);
136 }
137
138 if (show_unit) {
139 out.put('s');
140 }
141 }
143};
144
145// ============================================================================
146// Formatter for struct timeval (.tv_sec, .tv_usec)
147// ============================================================================
148
157template <typename T> struct formatter<T, std::enable_if_t<is_timeval_v<T>>> {
161 uint8_t precision{6}; // default: 6 digits (microseconds)
165 bool show_unit{true};
166
171 constexpr void parse(format_parse_context &ctx) noexcept {
172 auto spec = ctx.spec();
173 if (!spec.empty() && spec.front() == ':') {
174 spec.remove_prefix(1);
175 }
176 for (char c : spec) {
177 if (c == 'm' || c == '3')
178 precision = 3;
179 else if (c == 'u' || c == '6')
180 precision = 6;
181 else if (c == 'r' || c == 'R')
182 show_unit = false;
183 }
184 }
185
191 // time-field arithmetic converts between C/POSIX integer widths whose ranges are bounded by the format.
192 RELOCO_BEGIN_UNSAFE_CODE
193 void format(const T &tv, const sink &out) const noexcept {
194 int64_t sec = static_cast<int64_t>(tv.tv_sec);
195 uint32_t usec = static_cast<uint32_t>(tv.tv_usec);
196
197 if (sec < 0) {
198 out.put('-');
199 detail::format_unsigned<detail::radix::decimal>(out, static_cast<uint64_t>(-sec), false, 0);
200 } else {
201 detail::format_unsigned<detail::radix::decimal>(out, static_cast<uint64_t>(sec), false, 0);
202 }
203
204 out.put('.');
205 if (precision == 3) {
206 detail::format_unsigned<detail::radix::decimal>(out, usec / 1'000, false, 3);
207 } else {
208 detail::format_unsigned<detail::radix::decimal>(out, usec, false, 6);
209 }
210
211 if (show_unit) {
212 out.put('s');
213 }
214 }
216};
217
218} // namespace microfmt
Definition microfmt.hpp:881
Definition assert.hpp:19
constexpr bool is_timeval_v
Variable template for detail::is_timeval_like.
Definition posix_time.hpp:66
constexpr bool is_timespec_v
Variable template for detail::is_timespec_like.
Definition posix_time.hpp:59
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
SFINAE trait detecting timespec-like types with tv_sec/tv_nsec.
Definition posix_time.hpp:26
SFINAE trait detecting timeval-like types with tv_sec/tv_usec.
Definition posix_time.hpp:41
RELOCO_BEGIN_UNSAFE_CODE void format(const T &ts, const sink &out) const noexcept
Renders a timespec as sec.frac with optional s unit.
Definition posix_time.hpp:118
constexpr void parse(format_parse_context &ctx) noexcept
Parses precision and unit flags from the format specifier.
Definition posix_time.hpp:94
RELOCO_BEGIN_UNSAFE_CODE void format(const T &tv, const sink &out) const noexcept
Renders a timeval as sec.frac with optional s unit.
Definition posix_time.hpp:193
constexpr void parse(format_parse_context &ctx) noexcept
Parses precision and unit flags from the format specifier.
Definition posix_time.hpp:171
Definition microfmt.hpp:985