From: Joel Rosdahl Date: Sun, 26 Oct 2025 20:54:48 +0000 (+0100) Subject: enhance: Add util::format_duration X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=175aa7d6cdf0193c9b61c235ef151038943c4709;p=thirdparty%2Fccache.git enhance: Add util::format_duration --- diff --git a/src/ccache/util/string.cpp b/src/ccache/util/string.cpp index 048451aa..86b783c8 100644 --- a/src/ccache/util/string.cpp +++ b/src/ccache/util/string.cpp @@ -173,6 +173,36 @@ format_digest(nonstd::span data) {data.data() + base16_bytes, data.size() - base16_bytes}); } +std::string +format_duration(std::chrono::milliseconds ms) +{ + const auto ms_count = ms.count(); + + if (ms_count == 0) { + return "0s"; + } + + // Try to express in the largest unit that divides evenly. + constexpr int64_t ms_per_day = 24 * 60 * 60 * 1000; + constexpr int64_t ms_per_hour = 60 * 60 * 1000; + constexpr int64_t ms_per_minute = 60 * 1000; + constexpr int64_t ms_per_second = 1000; + + if (ms_count % ms_per_day == 0) { + return FMT("{}d", ms_count / ms_per_day); + } + if (ms_count % ms_per_hour == 0) { + return FMT("{}h", ms_count / ms_per_hour); + } + if (ms_count % ms_per_minute == 0) { + return FMT("{}m", ms_count / ms_per_minute); + } + if (ms_count % ms_per_second == 0) { + return FMT("{}s", ms_count / ms_per_second); + } + return FMT("{}ms", ms_count); +} + std::string format_human_readable_diff(int64_t diff, SizeUnitPrefixType prefix_type) { diff --git a/src/ccache/util/string.hpp b/src/ccache/util/string.hpp index 59111048..e8c4989d 100644 --- a/src/ccache/util/string.hpp +++ b/src/ccache/util/string.hpp @@ -78,6 +78,9 @@ std::string format_base32hex(nonstd::span data); // base32hex digits without padding characters. std::string format_digest(nonstd::span data); +// Format `ms` as a duration string. +std::string format_duration(std::chrono::milliseconds ms); + // Format `diff` as a human-readable string. std::string format_human_readable_diff(int64_t diff, SizeUnitPrefixType prefix_type); diff --git a/unittest/test_util_string.cpp b/unittest/test_util_string.cpp index 21940e0f..53df10a9 100644 --- a/unittest/test_util_string.cpp +++ b/unittest/test_util_string.cpp @@ -105,6 +105,61 @@ TEST_CASE("util::format_base16") CHECK(util::format_base16({data, sizeof(data)}) == "00010203"); } +TEST_CASE("util::format_duration") +{ + CHECK(util::format_duration(0ms) == "0s"); + CHECK(util::format_duration(1ms) == "1ms"); + CHECK(util::format_duration(999ms) == "999ms"); + CHECK(util::format_duration(1000ms) == "1s"); + CHECK(util::format_duration(1002ms) == "1002ms"); + CHECK(util::format_duration(7000ms) == "7s"); + + CHECK(util::format_duration(0s) == "0s"); + CHECK(util::format_duration(5s) == "5s"); + CHECK(util::format_duration(59s) == "59s"); + CHECK(util::format_duration(60s) == "1m"); + CHECK(util::format_duration(61s) == "61s"); + CHECK(util::format_duration(70s) == "70s"); + CHECK(util::format_duration(119s) == "119s"); + CHECK(util::format_duration(120s) == "2m"); + CHECK(util::format_duration(3599s) == "3599s"); + CHECK(util::format_duration(3600s) == "1h"); + CHECK(util::format_duration(3601s) == "3601s"); + CHECK(util::format_duration(5000s) == "5000s"); + CHECK(util::format_duration(7199s) == "7199s"); + CHECK(util::format_duration(7200s) == "2h"); + CHECK(util::format_duration(86399s) == "86399s"); + CHECK(util::format_duration(86400s) == "1d"); + CHECK(util::format_duration(86401s) == "86401s"); + CHECK(util::format_duration(172800s) == "2d"); + + CHECK(util::format_duration(4min) == "4m"); + CHECK(util::format_duration(60min) == "1h"); + CHECK(util::format_duration(90min) == "90m"); + + CHECK(util::format_duration(9h) == "9h"); + CHECK(util::format_duration(48h) == "2d"); + CHECK(util::format_duration(49h) == "49h"); + + auto test_round_trip = [](std::string_view str) { + auto parsed = util::parse_duration(str); + REQUIRE(parsed); + CHECK(util::format_duration(*parsed) == str); + }; + + test_round_trip("0s"); + test_round_trip("1ms"); + test_round_trip("500ms"); + test_round_trip("1s"); + test_round_trip("5s"); + test_round_trip("1m"); + test_round_trip("30m"); + test_round_trip("1h"); + test_round_trip("12h"); + test_round_trip("1d"); + test_round_trip("7d"); +} + TEST_CASE("util::parse_base16") { SUBCASE("empty string")