From 6af5c48d83c11e09595c49ee8e00f85a23712f7b Mon Sep 17 00:00:00 2001 From: gabime Date: Fri, 28 Nov 2025 15:48:07 +0200 Subject: [PATCH] Fix issue #3483 --- CMakeLists.txt | 6 +++- include/spdlog/details/os-inl.h | 38 ++------------------------ include/spdlog/pattern_formatter-inl.h | 12 +++++--- tests/test_pattern_formatter.cpp | 8 ++++-- 4 files changed, 21 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bca70042..7d71f1fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,8 @@ option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library." OFF) option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF) option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF) +option(SPDLOG_NO_TZ_OFFSET "Omit %z timezone offset (use on platforms without tm_gmtoff)" OFF) + if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO) message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") @@ -286,7 +288,9 @@ foreach( SPDLOG_NO_TLS SPDLOG_NO_ATOMIC_LEVELS SPDLOG_DISABLE_DEFAULT_LOGGER - SPDLOG_USE_STD_FORMAT) + SPDLOG_USE_STD_FORMAT + SPDLOG_NO_TZ_OFFSET +) if(${SPDLOG_OPTION}) target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION}) target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION}) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 8a2e203c..fdbedcc0 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -244,6 +244,7 @@ SPDLOG_INLINE size_t filesize(FILE *f) { #endif // Return utc offset in minutes or throw spdlog_ex on failure +#if !defined(SPDLOG_NO_TZ_OFFSET) SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) { #ifdef _WIN32 #if _WIN32_WINNT < _WIN32_WINNT_WS08 @@ -263,46 +264,11 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) { } return offset; #else - - #if defined(sun) || defined(__sun) || defined(_AIX) || \ - (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \ - (!defined(__APPLE__) && !defined(_BSD_SOURCE) && !defined(_GNU_SOURCE) && \ - (!defined(_POSIX_VERSION) || (_POSIX_VERSION < 202405L))) - // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris - struct helper { - static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), - const std::tm &gmtm = details::os::gmtime()) { - int local_year = localtm.tm_year + (1900 - 1); - int gmt_year = gmtm.tm_year + (1900 - 1); - - long int days = ( - // difference in day of year - localtm.tm_yday - - gmtm.tm_yday - - // + intervening leap days - + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) + - ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) - - // + difference in years * 365 */ - + static_cast(local_year - gmt_year) * 365); - - long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); - long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); - long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); - - return secs; - } - }; - - auto offset_seconds = helper::calculate_gmt_offset(tm); - #else auto offset_seconds = tm.tm_gmtoff; - #endif - return static_cast(offset_seconds / 60); #endif } +#endif // SPDLOG_NO_TZ_OFFSET // Return current thread id as size_t // It exists because the std::this_thread::get_id() is much slower(especially diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index fd408ed5..2d9f7c92 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -510,6 +510,7 @@ public: }; // ISO 8601 offset from UTC in timezone (+-HH:MM) +// If SPDLOG_NO_TZ_OFFSET is defined, print "+??.??" instead. template class z_formatter final : public flag_formatter { public: @@ -524,6 +525,10 @@ public: const size_t field_size = 6; ScopedPadder p(field_size, padinfo_, dest); + #ifdef SPDLOG_NO_TZ_OFFSET + const char *str = "+??:??"; + dest.append(str, str + 6); + #else auto total_minutes = get_cached_offset(msg, tm_time); bool is_negative = total_minutes < 0; if (is_negative) { @@ -536,6 +541,7 @@ public: fmt_helper::pad2(total_minutes / 60, dest); // hours dest.push_back(':'); fmt_helper::pad2(total_minutes % 60, dest); // minutes + #endif // SPDLOG_NO_TZ_OFFSET } private: @@ -1154,12 +1160,10 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i formatters_.push_back(details::make_unique>(padding)); need_localtime_ = true; break; - case ('z'): // timezone - formatters_.push_back(details::make_unique>(padding)); - need_localtime_ = true; + formatters_.push_back(details::make_unique>(padding)); + need_localtime_ = true; break; - case ('P'): // pid formatters_.push_back(details::make_unique>(padding)); break; diff --git a/tests/test_pattern_formatter.cpp b/tests/test_pattern_formatter.cpp index 17a1bbcb..21e83e13 100644 --- a/tests/test_pattern_formatter.cpp +++ b/tests/test_pattern_formatter.cpp @@ -82,8 +82,12 @@ TEST_CASE("GMT offset ", "[pattern_formatter]") { const auto now = std::chrono::system_clock::now(); const auto yesterday = now - 24h; - REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc, - "\n") == "+00:00\n"); +#ifndef SPDLOG_NO_TZ_OFFSET + const std::string expected_result = "+00:00\n"; +#else + const std::string expected_result = "+??:??\n"; +#endif + REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc, "\n") == expected_result); } TEST_CASE("color range test1", "[pattern_formatter]") {