9 #include "quill/bundled/fmt/format.h" 10 #include "quill/core/Attributes.h" 11 #include "quill/core/Common.h" 12 #include "quill/core/QuillError.h" 13 #include "quill/core/TimeUtilities.h" 53 QUILL_ATTRIBUTE_COLD
void init(std::string timestamp_format, Timezone timezone)
55 _timestamp_format = std::move(timestamp_format);
56 _time_zone = timezone;
58 if (_timestamp_format.find(
"%X") != std::string::npos)
60 QUILL_THROW(
QuillError(
"`%X` as format modifier is not currently supported in format: " + _timestamp_format));
64 _replace_all(_timestamp_format,
"%r",
"%I:%M:%S %p");
65 _replace_all(_timestamp_format,
"%R",
"%H:%M");
66 _replace_all(_timestamp_format,
"%T",
"%H:%M:%S");
69 _populate_initial_parts(_timestamp_format);
73 QUILL_NODISCARD QUILL_ATTRIBUTE_HOT std::string
const& format_timestamp(time_t timestamp)
78 if (timestamp < _cached_timestamp)
80 _fallback_formatted = _safe_strftime(_timestamp_format.data(), timestamp, _time_zone).data();
81 return _fallback_formatted;
87 if (timestamp >= _next_recalculation_timestamp)
90 _pre_formatted_ts.clear();
91 _cached_indexes.clear();
94 _populate_pre_formatted_string_and_cached_indexes(timestamp);
96 if (_time_zone == Timezone::LocalTime)
101 _next_recalculation_timestamp = _next_quarter_hour_timestamp(timestamp);
103 else if (_time_zone == Timezone::GmtTime)
107 _next_recalculation_timestamp = _next_noon_or_midnight_timestamp(timestamp);
111 if (_cached_indexes.empty())
114 return _pre_formatted_ts;
117 if (_cached_timestamp == timestamp)
123 return _pre_formatted_ts;
127 time_t
const timestamp_diff = timestamp - _cached_timestamp;
130 _cached_timestamp = timestamp;
135 _cached_seconds +=
static_cast<uint32_t
>(timestamp_diff);
137 uint32_t total_seconds = _cached_seconds;
138 uint32_t
const hours = total_seconds / 3600;
139 total_seconds -= hours * 3600;
140 uint32_t
const minutes = total_seconds / 60;
141 total_seconds -= minutes * 60;
142 uint32_t
const seconds = total_seconds;
144 for (
auto const& index : _cached_indexes)
147 switch (index.second)
150 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}", hours);
153 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}", minutes);
156 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}", seconds);
159 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}",
160 (hours == 0 ? 12 : (hours > 12 ? hours - 12 : hours)));
163 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:2}",
164 (hours == 0 ? 12 : (hours > 12 ? hours - 12 : hours)));
167 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:2}", hours);
170 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:10}", _cached_timestamp);
177 return _pre_formatted_ts;
181 enum class format_type : uint8_t
193 QUILL_ATTRIBUTE_COLD
void _populate_initial_parts(std::string timestamp_format)
199 auto const [part1, part2] = _split_timestamp_format_once(timestamp_format);
203 _initial_parts.push_back(part1);
208 _initial_parts.push_back(part2);
211 if (part1.empty() && part2.empty())
216 if (!timestamp_format.empty())
218 _initial_parts.push_back(timestamp_format);
226 void _populate_pre_formatted_string_and_cached_indexes(time_t timestamp)
228 _cached_timestamp = timestamp;
232 if (_time_zone == Timezone::LocalTime)
234 localtime_rs(reinterpret_cast<time_t const*>(std::addressof(_cached_timestamp)), std::addressof(time_info));
236 else if (_time_zone == Timezone::GmtTime)
238 gmtime_rs(reinterpret_cast<time_t const*>(std::addressof(_cached_timestamp)), std::addressof(time_info));
243 static_cast<uint32_t
>((time_info.tm_hour * 3600) + (time_info.tm_min * 60) + time_info.tm_sec);
246 for (
auto const& format_part : _initial_parts)
249 _pre_formatted_ts += _safe_strftime(format_part.data(), _cached_timestamp, _time_zone).data();
253 if (format_part ==
"%H")
255 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::H);
257 else if (format_part ==
"%M")
259 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::M);
261 else if (format_part ==
"%S")
263 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::S);
265 else if (format_part ==
"%I")
267 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::I);
269 else if (format_part ==
"%k")
271 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::k);
273 else if (format_part ==
"%l")
275 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::l);
277 else if (format_part ==
"%s")
279 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 10, format_type::s);
285 std::pair<std::string, std::string>
static _split_timestamp_format_once(std::string& timestamp_format) noexcept
288 std::array<std::string, 7>
const modifiers{
"%H",
"%M",
"%S",
"%I",
"%k",
"%l",
"%s"};
294 std::map<size_t, std::string> found_format_modifiers;
296 for (
auto const& modifier : modifiers)
298 if (
auto const search = timestamp_format.find(modifier); search != std::string::npos)
301 found_format_modifiers.emplace(search, modifier);
305 if (found_format_modifiers.empty())
309 return std::make_pair(std::string{}, std::string{});
317 std::string
const part_1 = found_format_modifiers.begin()->first > 0
318 ? std::string{timestamp_format.data(), found_format_modifiers.begin()->first}
322 std::string
const part_2 = found_format_modifiers.begin()->second;
326 timestamp_format = std::string{timestamp_format.data() + found_format_modifiers.begin()->first + 2};
328 return std::make_pair(part_1, part_2);
332 QUILL_NODISCARD
static std::vector<char> _safe_strftime(
char const* format_string, time_t timestamp, Timezone timezone)
334 if (format_string[0] ==
'\0')
336 std::vector<char> res;
343 if (timezone == Timezone::LocalTime)
345 localtime_rs(reinterpret_cast<time_t const*>(std::addressof(timestamp)), std::addressof(time_info));
347 else if (timezone == Timezone::GmtTime)
349 gmtime_rs(reinterpret_cast<time_t const*>(std::addressof(timestamp)), std::addressof(time_info));
355 size_t res = strftime(&buffer[0], buffer.size(), format_string, std::addressof(time_info));
360 buffer.resize(buffer.size() * 2);
361 res = strftime(&buffer[0], buffer.size(), format_string, std::addressof(time_info));
368 static void _replace_all(std::string& str, std::string
const& old_value, std::string
const& new_value) noexcept
370 std::string::size_type pos = 0u;
371 while ((pos = str.find(old_value, pos)) != std::string::npos)
373 str.replace(pos, old_value.length(), new_value);
374 pos += new_value.length();
379 QUILL_NODISCARD
static time_t _nearest_quarter_hour_timestamp(time_t timestamp) noexcept
381 time_t
const nearest_quarter_hour_ts = (timestamp / 900) * 900;
382 return nearest_quarter_hour_ts;
386 QUILL_NODISCARD
static time_t _next_quarter_hour_timestamp(time_t timestamp) noexcept
388 time_t
const next_quarter_hour_ts = _nearest_quarter_hour_timestamp(timestamp) + 900;
389 return next_quarter_hour_ts;
393 QUILL_NODISCARD
static time_t _next_noon_or_midnight_timestamp(time_t timestamp) noexcept
399 if (time_info.tm_hour < 12)
402 time_info.tm_hour = 11;
403 time_info.tm_min = 59;
404 time_info.tm_sec = 59;
409 time_info.tm_hour = 23;
410 time_info.tm_min = 59;
411 time_info.tm_sec = 59;
415 std::chrono::system_clock::time_point
const next_midnight =
416 std::chrono::system_clock::from_time_t(
detail::timegm(&time_info));
419 return std::chrono::duration_cast<std::chrono::seconds>(next_midnight.time_since_epoch()).count() + 1;
425 std::vector<std::string> _initial_parts;
428 std::vector<std::pair<size_t, format_type>> _cached_indexes;
431 std::string _timestamp_format;
434 std::string _pre_formatted_ts;
437 std::string _fallback_formatted;
440 time_t _next_recalculation_timestamp{0};
443 time_t _cached_timestamp{0};
446 uint32_t _cached_seconds{0};
449 Timezone _time_zone{Timezone::GmtTime};
tm * localtime_rs(time_t const *timer, tm *buf)
Portable localtime_r or _s per operating system.
Definition: TimeUtilities.h:59
A class that converts a timestamp to a string based on the given format.
Definition: StringFromTime.h:49
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
tm * gmtime_rs(time_t const *timer, tm *buf)
Portable gmtime_r or _s per operating system.
Definition: TimeUtilities.h:31
time_t timegm(tm *tm)
inverses of gmtime
Definition: TimeUtilities.h:85
custom exception
Definition: QuillError.h:45
A contiguous memory buffer with an optional growing ability.
Definition: base.h:1751