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;
145 bool const hours_changed = (hours != _prev_hours);
146 bool const minutes_changed = (minutes != _prev_minutes);
147 bool const seconds_changed = (seconds != _prev_seconds);
151 _prev_minutes = minutes;
152 _prev_seconds = seconds;
154 for (
auto const& index : _cached_indexes)
157 switch (index.second)
162 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}", hours);
168 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}", minutes);
174 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}", seconds);
180 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:02}",
181 (hours == 0 ? 12 : (hours > 12 ? hours - 12 : hours)));
187 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:2}",
188 (hours == 0 ? 12 : (hours > 12 ? hours - 12 : hours)));
194 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:2}", hours);
199 fmtquill::format_to(&_pre_formatted_ts[index.first],
"{:10}", _cached_timestamp);
206 return _pre_formatted_ts;
210 enum class format_type : uint8_t
222 QUILL_ATTRIBUTE_COLD
void _populate_initial_parts(std::string timestamp_format)
228 auto const [part1, part2] = _split_timestamp_format_once(timestamp_format);
232 _initial_parts.push_back(part1);
237 _initial_parts.push_back(part2);
240 if (part1.empty() && part2.empty())
245 if (!timestamp_format.empty())
247 _initial_parts.push_back(timestamp_format);
255 void _populate_pre_formatted_string_and_cached_indexes(time_t timestamp)
257 _cached_timestamp = timestamp;
261 if (_time_zone == Timezone::LocalTime)
263 localtime_rs(reinterpret_cast<time_t const*>(std::addressof(_cached_timestamp)), std::addressof(time_info));
265 else if (_time_zone == Timezone::GmtTime)
267 gmtime_rs(reinterpret_cast<time_t const*>(std::addressof(_cached_timestamp)), std::addressof(time_info));
272 static_cast<uint32_t
>((time_info.tm_hour * 3600) + (time_info.tm_min * 60) + time_info.tm_sec);
275 uint32_t total_seconds = _cached_seconds;
276 _prev_hours = total_seconds / 3600;
277 total_seconds -= _prev_hours * 3600;
278 _prev_minutes = total_seconds / 60;
279 _prev_seconds = total_seconds - _prev_minutes * 60;
282 for (
auto const& format_part : _initial_parts)
285 _pre_formatted_ts += _safe_strftime(format_part.data(), _cached_timestamp, _time_zone).data();
289 if (format_part ==
"%H")
291 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::H);
293 else if (format_part ==
"%M")
295 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::M);
297 else if (format_part ==
"%S")
299 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::S);
301 else if (format_part ==
"%I")
303 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::I);
305 else if (format_part ==
"%k")
307 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::k);
309 else if (format_part ==
"%l")
311 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 2, format_type::l);
313 else if (format_part ==
"%s")
315 _cached_indexes.emplace_back(_pre_formatted_ts.size() - 10, format_type::s);
321 std::pair<std::string, std::string>
static _split_timestamp_format_once(std::string& timestamp_format) noexcept
324 std::array<std::string, 7>
const modifiers{
"%H",
"%M",
"%S",
"%I",
"%k",
"%l",
"%s"};
330 std::map<size_t, std::string> found_format_modifiers;
332 for (
auto const& modifier : modifiers)
334 if (
auto const search = timestamp_format.find(modifier); search != std::string::npos)
337 found_format_modifiers.emplace(search, modifier);
341 if (found_format_modifiers.empty())
345 return std::make_pair(std::string{}, std::string{});
353 std::string
const part_1 = found_format_modifiers.begin()->first > 0
354 ? std::string{timestamp_format.data(), found_format_modifiers.begin()->first}
358 std::string
const part_2 = found_format_modifiers.begin()->second;
362 timestamp_format = std::string{timestamp_format.data() + found_format_modifiers.begin()->first + 2};
364 return std::make_pair(part_1, part_2);
368 QUILL_NODISCARD
static std::vector<char> _safe_strftime(
char const* format_string, time_t timestamp, Timezone timezone)
370 if (format_string[0] ==
'\0')
372 std::vector<char> res;
379 if (timezone == Timezone::LocalTime)
381 localtime_rs(reinterpret_cast<time_t const*>(std::addressof(timestamp)), std::addressof(time_info));
383 else if (timezone == Timezone::GmtTime)
385 gmtime_rs(reinterpret_cast<time_t const*>(std::addressof(timestamp)), std::addressof(time_info));
391 size_t res = strftime(&buffer[0], buffer.size(), format_string, std::addressof(time_info));
396 buffer.resize(buffer.size() * 2);
397 res = strftime(&buffer[0], buffer.size(), format_string, std::addressof(time_info));
404 static void _replace_all(std::string& str, std::string
const& old_value, std::string
const& new_value) noexcept
406 std::string::size_type pos = 0u;
407 while ((pos = str.find(old_value, pos)) != std::string::npos)
409 str.replace(pos, old_value.length(), new_value);
410 pos += new_value.length();
415 QUILL_NODISCARD
static time_t _nearest_quarter_hour_timestamp(time_t timestamp) noexcept
417 time_t
const nearest_quarter_hour_ts = (timestamp / 900) * 900;
418 return nearest_quarter_hour_ts;
422 QUILL_NODISCARD
static time_t _next_quarter_hour_timestamp(time_t timestamp) noexcept
424 time_t
const next_quarter_hour_ts = _nearest_quarter_hour_timestamp(timestamp) + 900;
425 return next_quarter_hour_ts;
429 QUILL_NODISCARD
static time_t _next_noon_or_midnight_timestamp(time_t timestamp) noexcept
435 if (time_info.tm_hour < 12)
438 time_info.tm_hour = 11;
439 time_info.tm_min = 59;
440 time_info.tm_sec = 59;
445 time_info.tm_hour = 23;
446 time_info.tm_min = 59;
447 time_info.tm_sec = 59;
451 std::chrono::system_clock::time_point
const next_midnight =
452 std::chrono::system_clock::from_time_t(
detail::timegm(&time_info));
455 return std::chrono::duration_cast<std::chrono::seconds>(next_midnight.time_since_epoch()).count() + 1;
461 std::vector<std::string> _initial_parts;
464 std::vector<std::pair<size_t, format_type>> _cached_indexes;
467 std::string _timestamp_format;
470 std::string _pre_formatted_ts;
473 std::string _fallback_formatted;
476 time_t _next_recalculation_timestamp{0};
479 time_t _cached_timestamp{0};
482 uint32_t _cached_seconds{0};
485 uint32_t _prev_hours{0};
486 uint32_t _prev_minutes{0};
487 uint32_t _prev_seconds{0};
490 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:1777