chore: bump {fmt} to 11.2.0 and fix compatibility (#7612)

* chore: bump fmt to 11.2.0

Acquire 6797f0c39a, which fixes compile error on GCC 15.1.

Acquire
9f269062a7,
which fixes buffer overflow in NetBSD 10.1 on GCC 10.5.

Replace `fmt::localtime` with `std::localtime`, as `fmt::` is now
deprecated.

* fix: format timezone ourselves on Windows

{fmt} 11.2.0 removed the ability to format `std::tm` with the format specifier `%z` on Windows, for a good reason. Ref: https://github.com/fmtlib/fmt/issues/4444

This forces us to implement our own solution on Windows as there's no alternative.

* fix: support `FMT_USE_EXCEPTIONS`
This commit is contained in:
Yat Ho
2025-10-15 01:53:08 +08:00
committed by GitHub
parent 518816ccc5
commit fb25228f24
8 changed files with 61 additions and 20 deletions

View File

@@ -1,10 +1,21 @@
add_library(fmt::fmt-header-only INTERFACE IMPORTED)
set(${CMAKE_FIND_PACKAGE_NAME}_INCLUDE "${TR_THIRD_PARTY_SOURCE_DIR}/fmt/include")
target_include_directories(fmt::fmt-header-only
INTERFACE
${TR_THIRD_PARTY_SOURCE_DIR}/fmt/include)
${${CMAKE_FIND_PACKAGE_NAME}_INCLUDE})
file(READ "${${CMAKE_FIND_PACKAGE_NAME}_INCLUDE}/fmt/base.h" _FMT_BASE_H)
if(_FMT_BASE_H MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
# Use math to skip leading zeros if any.
math(EXPR _FMT_VERSION_MAJOR ${CMAKE_MATCH_1})
math(EXPR _FMT_VERSION_MINOR ${CMAKE_MATCH_2})
math(EXPR _FMT_VERSION_PATCH ${CMAKE_MATCH_3})
set(${CMAKE_FIND_PACKAGE_NAME}_VERSION "${_FMT_VERSION_MAJOR}.${_FMT_VERSION_MINOR}.${_FMT_VERSION_PATCH}")
endif()
target_compile_definitions(fmt::fmt-header-only
INTERFACE
FMT_EXCEPTIONS=0
$<IF:$<VERSION_GREATER_EQUAL:${${CMAKE_FIND_PACKAGE_NAME}_VERSION},11.2.0>,FMT_USE_EXCEPTIONS,FMT_EXCEPTIONS>=0
FMT_HEADER_ONLY=1)

View File

@@ -52,6 +52,7 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdlib> // abort()
#include <iterator>
@@ -611,12 +612,12 @@ void gtr_text_buffer_set_text(Glib::RefPtr<Gtk::TextBuffer> const& b, Glib::ustr
[[nodiscard]] std::string get_date_string(time_t t)
{
return t == 0 ? _("N/A") : fmt::format("{:%x}", fmt::localtime(t));
return t == 0 ? _("N/A") : fmt::format("{:%x}", *std::localtime(&t));
}
[[nodiscard]] std::string get_date_time_string(time_t t)
{
return t == 0 ? _("N/A") : fmt::format("{:%c}", fmt::localtime(t));
return t == 0 ? _("N/A") : fmt::format("{:%c}", *std::localtime(&t));
}
} // namespace

View File

@@ -14,6 +14,10 @@
#include <string_view>
#include <utility>
#ifdef _WIN32
#include <windows.h> // GetTimeZoneInformation
#endif
#ifdef __ANDROID__
#include <android/log.h>
#endif
@@ -26,7 +30,7 @@
#include "libtransmission/file.h"
#include "libtransmission/log.h"
#include "libtransmission/tr-assert.h"
#include "libtransmission/tr-strbuf.h"
#include "libtransmission/tr-macros.h"
#include "libtransmission/utils.h"
using namespace std::literals;
@@ -194,17 +198,37 @@ void tr_logFreeQueue(tr_log_message* freeme)
// ---
std::string_view tr_logGetTimeStr(std::chrono::system_clock::time_point now, char* buf, size_t buflen)
std::string_view tr_logGetTimeStr(std::chrono::system_clock::time_point const now, char* const buf, size_t const buflen)
{
auto const now_tm = fmt::localtime(std::chrono::system_clock::to_time_t(now));
auto const subseconds = now - std::chrono::time_point_cast<std::chrono::seconds>(now);
auto const [out, len] = fmt::format_to_n(
buf,
buflen,
"{0:%FT%T.}{1:0>3%Q}{0:%z}",
now_tm,
std::chrono::duration_cast<std::chrono::milliseconds>(subseconds));
return { buf, len };
auto* walk = buf;
auto const now_time_t = std::chrono::system_clock::to_time_t(now);
auto const now_tm = *std::localtime(&now_time_t);
walk = fmt::format_to_n(
walk,
buflen,
"{0:%FT%R:}{1:%S}" TR_IF_WIN32("", "{0:%z}"),
now_tm,
std::chrono::time_point_cast<std::chrono::milliseconds>(now))
.out;
#ifdef _WIN32
if (auto tz_info = TIME_ZONE_INFORMATION{}; GetTimeZoneInformation(&tz_info) != TIME_ZONE_ID_INVALID)
{
// https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-gettimezoneinformation
// All translations between UTC time and local time are based on the following formula:
// UTC = local time + bias
// The bias is the difference, in minutes, between UTC time and local time.
auto const offset = tz_info.Bias < 0 ? -tz_info.Bias : tz_info.Bias;
walk = fmt::format_to_n(
walk,
buflen - (walk - buf),
"{:c}{:02d}{:02d}",
tz_info.Bias < 0 ? '+' : '-',
offset / 60,
offset % 60)
.out;
}
#endif
return { buf, static_cast<size_t>(walk - buf) };
}
std::string_view tr_logGetTimeStr(char* buf, size_t buflen)

View File

@@ -3,6 +3,7 @@
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <chrono>
#include <cstddef> // size_t
#include <ctime>
#include <utility>
@@ -78,7 +79,7 @@ void tr_session_alt_speeds::set_active(bool active, ChangeReason reason, bool fo
[[nodiscard]] bool tr_session_alt_speeds::is_active_minute(time_t time) const noexcept
{
auto const tm = fmt::localtime(time);
auto const tm = *std::localtime(&time);
size_t minute_of_the_week = (tm.tm_wday * MinutesPerDay) + (tm.tm_hour * MinutesPerHour) + tm.tm_min;

View File

@@ -6,6 +6,7 @@
#include <algorithm>
#include <array>
#include <cerrno> // EINVAL
#include <chrono>
#include <cstddef> // size_t
#include <ctime>
#include <map>
@@ -373,6 +374,8 @@ void torrentCallScript(tr_torrent const* tor, std::string const& script)
return;
}
auto const now = tr_time();
auto torrent_dir = tr_pathbuf{ tor->current_dir() };
tr_sys_path_native_separators(std::data(torrent_dir));
@@ -382,7 +385,7 @@ void torrentCallScript(tr_torrent const* tor, std::string const& script)
auto const labels_str = build_labels_string(tor->labels());
auto const trackers_str = buildTrackersString(tor);
auto const bytes_downloaded_str = std::to_string(tor->bytes_downloaded_.ever());
auto const localtime_str = fmt::format("{:%a %b %d %T %Y%n}", fmt::localtime(tr_time()));
auto const localtime_str = fmt::format("{:%a %b %d %T %Y%n}", *std::localtime(&now));
auto const priority_str = std::to_string(tor->get_priority());
auto const env = std::map<std::string_view, std::string_view>{

View File

@@ -7,6 +7,7 @@
#include <array>
#include <cctype> /* isspace */
#include <cmath> // floor
#include <chrono>
#include <cstdint> // int64_t
#include <cstdio>
#include <cstdlib>
@@ -883,7 +884,7 @@ template<size_t N>
std::string_view format_date(std::array<char, N>& buf, time_t now)
{
auto begin = std::data(buf);
auto end = fmt::format_to_n(begin, N, "{:%a %b %d %T %Y}", fmt::localtime(now)).out;
auto end = fmt::format_to_n(begin, N, "{:%a %b %d %T %Y}", *std::localtime(&now)).out;
return { begin, static_cast<size_t>(end - begin) };
}

View File

@@ -165,7 +165,7 @@ int parseCommandLine(app_opts& opts, int argc, char const* const* argv)
[[nodiscard]] auto toString(time_t now)
{
return now == 0 ? "Unknown" : fmt::format("{:%a %b %d %T %Y}", fmt::localtime(now));
return now == 0 ? "Unknown" : fmt::format("{:%a %b %d %T %Y}", *std::localtime(&now));
}
bool compareSecondField(std::string_view l, std::string_view r)