]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
enhance: Add util::format_duration
authorJoel Rosdahl <joel@rosdahl.net>
Sun, 26 Oct 2025 20:54:48 +0000 (21:54 +0100)
committerJoel Rosdahl <joel@rosdahl.net>
Mon, 3 Nov 2025 20:03:29 +0000 (21:03 +0100)
src/ccache/util/string.cpp
src/ccache/util/string.hpp
unittest/test_util_string.cpp

index 048451aa10aeaa0996305c762a441cf958f3f17b..86b783c8a485d2df2dae3e8180e3401919059e03 100644 (file)
@@ -173,6 +173,36 @@ format_digest(nonstd::span<const uint8_t> 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)
 {
index 591110486a7449fc16f76688a16573d1c7245d19..e8c4989d2724c970a9242c57a04444e89c0ff737 100644 (file)
@@ -78,6 +78,9 @@ std::string format_base32hex(nonstd::span<const uint8_t> data);
 // base32hex digits without padding characters.
 std::string format_digest(nonstd::span<const uint8_t> 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);
index 21940e0f94ffdedd15903e278fa086240509a452..53df10a9e30f3ee10c32144db25bd48b3004afd4 100644 (file)
@@ -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")